使用PhotoSauce.MagicScaler处理图片

MagicScaler是一个专为.NET平台设计的高性能图像处理库,其核心目标是让复杂的图像处理任务变得简单易行。它采用业界领先的算法,以线性光处理和锐化技术提供顶级的缩放质量。在速度与效率方面,MagicScaler在.NET生态中独领风骚。

适用业务场景:

1、快速且高质量地将大尺寸图片转换成适合网页显示的小图。

2、适应各种社交媒体平台的图像尺寸要求,自动调整图片大小并保持清晰度。

3、对于内存和计算资源有限的设备,MagicScaler可以有效减小图像处理的负担。

4、大量图片的自动化处理,如图片格式转换,尺寸调整等。

如下所示是用于接收字节大尺寸图片后进行缩放处理的方法:

复制代码
using PhotoSauce.MagicScaler;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;

namespace CentralizedDisplayDemo.TCPCcd
{
    /// <summary>
    /// CCD图像处理与信息绘制(PhotoSauce MagicScaler高性能版本)
    /// </summary>
    public class PhotoSauceImage : IDisposable
    {
        private bool _disposed;

        // 缓存GDI+资源
        private readonly Font _titleFont;
        private readonly Font _normalFont;
        private readonly Font _largeFont;

        private const int PEN_WIDTH = 1;
        private const int BRIGHTNESS_CIRCLE_RADIUS = 40;
        private const int TEXT_OFFSET_X = 100;
        private const int TEXT_OFFSET_Y_BASE = 50;
        private const int TEXT_LINE_SPACING = 150;

        private readonly SolidBrush _redBrush;
        private readonly SolidBrush _blueBrush;
        private readonly SolidBrush _lightBlueBrush;
        private readonly Pen _greenPen;
        private readonly Pen _redPen;
        private readonly Pen _arrowPen;

        private float _scale = 0.2f;
        private static int _imageWidth = 2048;
        private static int _imageHeight = 1500;

        /// <summary>
        /// 高质量配置(最佳清晰度,适合最终输出)
        /// 使用 Lanczos 算法 + 自动锐化
        /// </summary>
        private static readonly ProcessImageSettings _highQualitySettings = new ProcessImageSettings
        {
            Width = _imageWidth,
            Height = _imageHeight,
            ResizeMode = CropScaleMode.Stretch,
            Interpolation = InterpolationSettings.Lanczos,  // 最高质量,适合照片缩放
            Sharpen = true,                                 // 启用自动锐化补偿
            ColorProfileMode = ColorProfileMode.Normalize,
            HybridMode = HybridScaleMode.FavorQuality
        };

        /// <summary>
        /// 平衡配置(质量与性能的折中)
        /// 使用 Mitchell 算法,比 Lanczos 快约 30%,质量接近
        /// </summary>
        private static readonly ProcessImageSettings _balancedSettings = new ProcessImageSettings
        {
            Width = _imageWidth,
            Height = _imageHeight,
            ResizeMode = CropScaleMode.Stretch,
            Interpolation = InterpolationSettings.Mitchell,  // Mitchell-Netravali,高质量
            Sharpen = true,
            ColorProfileMode = ColorProfileMode.Normalize,
            HybridMode = HybridScaleMode.FavorQuality
        };

        /// <summary>
        /// 高性能配置(优先速度,适合实时预览)
        /// 使用 CatmullRom/Cubic 算法,速度快,质量尚可
        /// </summary>
        private static readonly ProcessImageSettings _fastSettings = new ProcessImageSettings
        {
            Width = _imageWidth,
            Height = _imageHeight,
            ResizeMode = CropScaleMode.Stretch,
            Interpolation = InterpolationSettings.Cubic,    // 双三次插值,速度快
            Sharpen = false,                                // 关闭锐化提升速度
            ColorProfileMode = ColorProfileMode.Normalize,
            HybridMode = HybridScaleMode.FavorSpeed
        };

        /// <summary>
        /// 极限速度配置(最低延迟,适合4帧/秒以上实时流)
        /// 使用 Linear/Bilinear 算法,牺牲质量换取速度
        /// </summary>
        private static readonly ProcessImageSettings _ultraFastSettings = new ProcessImageSettings
        {
            Width = _imageWidth,
            Height = _imageHeight,
            ResizeMode = CropScaleMode.Stretch,
            Interpolation = InterpolationSettings.Linear,   // 双线性插值,最快
            Sharpen = false,
            ColorProfileMode = ColorProfileMode.Normalize,
            HybridMode = HybridScaleMode.FavorSpeed
        };

        public PhotoSauceImage()
        {
            _titleFont = new Font("宋体", 14, FontStyle.Bold);
            _normalFont = new Font("宋体", 14, FontStyle.Bold);
            _largeFont = new Font("宋体", 60, FontStyle.Bold);

            _redBrush = new SolidBrush(Color.Crimson);
            _blueBrush = new SolidBrush(Color.Tomato);
            _lightBlueBrush = new SolidBrush(Color.LightBlue);
            _greenPen = new Pen(Color.Lime, PEN_WIDTH);
            _redPen = new Pen(Color.Crimson, PEN_WIDTH);
            _arrowPen = new Pen(Color.Crimson, 1);
            _arrowPen.CustomEndCap = new AdjustableArrowCap(2, 2);
        }

        /// <summary>
        /// 高质量版本(推荐日常使用)
        /// 清晰度:★★★★★ 性能:★★★★
        /// </summary>
        public Bitmap ConvertBytesToBitmapHighQuality(byte[] imageBytes, ROIModel roi)
        {
            return ProcessImage(imageBytes, roi, _highQualitySettings);
        }

        /// <summary>
        /// 平衡版本(推荐实时显示)
        /// 清晰度:★★★★ 性能:★★★★★
        /// </summary>
        public Bitmap ConvertBytesToBitmapBalanced(byte[] imageBytes, ROIModel roi)
        {
            return ProcessImage(imageBytes, roi, _balancedSettings);
        }

        /// <summary>
        /// 快速版本(高帧率场景,4帧/秒以上)
        /// 清晰度:★★★ 性能:★★★★★
        /// </summary>
        public Bitmap ConvertBytesToBitmapFast(byte[] imageBytes, ROIModel roi)
        {
            return ProcessImage(imageBytes, roi, _fastSettings);
        }

        /// <summary>
        /// 极限速度版本(8帧/秒以上)
        /// 清晰度:★★ 性能:★★★★★
        /// </summary>
        public Bitmap ConvertBytesToBitmapUltraFast(byte[] imageBytes, ROIModel roi)
        {
            return ProcessImage(imageBytes, roi, _ultraFastSettings);
        }

        /// <summary>
        /// 核心处理方法
        /// </summary>
        private Bitmap ProcessImage(byte[] imageBytes, ROIModel roi, ProcessImageSettings settings)
        {
            if (imageBytes == null || imageBytes.Length == 0)
            {
                throw new ArgumentNullException(nameof(imageBytes));
            }

            using (var inputStream = new MemoryStream(imageBytes))
            using (var outputStream = new MemoryStream())
            {
                // MagicScaler 解码并缩放
                MagicImageProcessor.ProcessImage(inputStream, outputStream, settings);

                outputStream.Seek(0, SeekOrigin.Begin);
                using (var scaledImage = Image.FromStream(outputStream))
                {
                    _scale = (float)scaledImage.Width / 4096;
                    _imageWidth = scaledImage.Width;
                    _imageHeight = scaledImage.Height;

                    var resultBitmap = new Bitmap(_imageWidth, _imageHeight, PixelFormat.Format24bppRgb);
                    using (var graphics = Graphics.FromImage(resultBitmap))
                    {
                        graphics.CompositingQuality = CompositingQuality.HighSpeed;
                        graphics.InterpolationMode = InterpolationMode.NearestNeighbor;
                        graphics.PixelOffsetMode = PixelOffsetMode.None;
                        graphics.SmoothingMode = SmoothingMode.HighSpeed;
                        graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixel;

                        graphics.DrawImage(scaledImage, 0, 0, _imageWidth, _imageHeight);

                        if (roi != null)
                        {
                            graphics.SmoothingMode = SmoothingMode.AntiAlias;
                            DrawROI(graphics, roi);
                            DrawResults(graphics, roi);
                            DrawTextInfo(graphics, roi);
                            graphics.SmoothingMode = SmoothingMode.HighSpeed;
                        }
                    }
                    return resultBitmap;
                }
            }
        }

        /// <summary>
        /// 兼容原有接口(默认使用平衡配置)
        /// </summary>
        public async Task<Bitmap> ConvertBytesToBitmapAsync(byte[] imageBytes, ROIModel roi)
        {
            return await Task.Run(() => ConvertBytesToBitmapHighQuality(imageBytes, roi));
        }

        #region 绘制方法

        private void DrawROI(Graphics graphics, ROIModel roi)
        {
            var step = roi.ParseInt("ss", 0);
            var status = roi.ParseInt("ss", 1);

            var x1 = roi.ParseInt("mm", 0) * _scale;
            var y1 = roi.ParseInt("mm", 1) * _scale;
            var x2 = roi.ParseInt("mm", 2) * _scale;
            var y2 = roi.ParseInt("mm", 3) * _scale;
            var x3 = roi.ParseInt("mm", 4) * _scale;
            var y3 = roi.ParseInt("mm", 5) * _scale;
            var x4 = roi.ParseInt("mm", 6) * _scale;
            var y4 = roi.ParseInt("mm", 7) * _scale;

            if (step != 23 && x3 > 0 && y3 > 0)
            {
                graphics.DrawLine(_redPen, x1, y1, x2, y2);
                graphics.DrawLine(_greenPen, x3, y3, x4, y4);
                graphics.DrawLine(_arrowPen, x1, y1, x3, y3);
                DrawVerticalLine(graphics, _redPen, x3, y3, 4);
                graphics.DrawLine(_arrowPen, x2, y2, x4, y4);
                DrawVerticalLine(graphics, _redPen, x4, y4, 4);
            }

            if (status == 3)
            {
                DrawDiametricLines(graphics, roi);
            }
            else
            {
                DrawSeedCrystalRect(graphics, roi);
            }
        }

        private void DrawVerticalLine(Graphics g, Pen pen, float centerX, float centerY, float height)
        {
            float halfHeight = height / 2f;
            g.DrawLine(pen, centerX, centerY - halfHeight, centerX, centerY + halfHeight);
        }

        private void DrawDiametricLines(Graphics graphics, ROIModel roi)
        {
            graphics.DrawLine(_greenPen,
                roi.ParseInt("bm", 0) * _scale, roi.ParseInt("bm", 1) * _scale,
                roi.ParseInt("bm", 2) * _scale, roi.ParseInt("bm", 3) * _scale);
            graphics.DrawLine(_redPen,
                roi.ParseInt("bm", 4) * _scale, roi.ParseInt("bm", 5) * _scale,
                roi.ParseInt("bm", 6) * _scale, roi.ParseInt("bm", 7) * _scale);
            graphics.DrawLine(_greenPen,
                roi.ParseInt("bm", 8) * _scale, roi.ParseInt("bm", 9) * _scale,
                roi.ParseInt("bm", 10) * _scale, roi.ParseInt("bm", 11) * _scale);
            graphics.DrawLine(_redPen,
                roi.ParseInt("bm", 12) * _scale, roi.ParseInt("bm", 13) * _scale,
                roi.ParseInt("bm", 14) * _scale, roi.ParseInt("bm", 15) * _scale);
        }

        private void DrawSeedCrystalRect(Graphics graphics, ROIModel roi)
        {
            int x1 = (int)(roi.ParseInt("sm", 0) * _scale);
            int y1 = (int)(roi.ParseInt("sm", 1) * _scale);
            int x2 = (int)(roi.ParseInt("sm", 2) * _scale);
            int y2 = (int)(roi.ParseInt("sm", 3) * _scale);
            int x3 = (int)(roi.ParseInt("sm", 4) * _scale);
            int y3 = (int)(roi.ParseInt("sm", 5) * _scale);
            int x4 = (int)(roi.ParseInt("sm", 6) * _scale);
            int y4 = (int)(roi.ParseInt("sm", 7) * _scale);

            int heighty = y1 + Math.Abs(y1 - y2) / 2;
            int width = Math.Abs(x1 - x2);
            int height = Math.Abs(y1 - y2);

            graphics.DrawRectangle(_greenPen, x1, y1, width, height);
            graphics.DrawLine(_redPen, x1, heighty, x3, heighty);
            graphics.DrawLine(_redPen, x4, heighty, x1 + width, heighty);
            graphics.DrawLine(_redPen, x3, y1, x3, y1 + height);
            graphics.DrawLine(_redPen, x4, y1, x4, y1 + height);
        }

        private void DrawResults(Graphics graphics, ROIModel roi)
        {
            int brightnessX = roi.ParseInt("br", 0);
            int brightnessY = roi.ParseInt("br", 1);

            float radius = BRIGHTNESS_CIRCLE_RADIUS * _scale;
            graphics.DrawEllipse(_redPen,
                brightnessX * _scale - radius,
                brightnessY * _scale - radius,
                radius * 2, radius * 2);
        }

        private void DrawTextInfo(Graphics graphics, ROIModel roi)
        {
            float yPos = TEXT_OFFSET_Y_BASE * _scale;
            float xOffset = TEXT_OFFSET_X * _scale;

            graphics.DrawString($"直径: {roi.SD[3]}pix/{roi.SD[4]}mm", _titleFont, _redBrush, xOffset, yPos);
            yPos += TEXT_LINE_SPACING * _scale;

            graphics.DrawString($"液口距: {roi.SD[5]}pix/{roi.SD[6]}mm", _titleFont, _redBrush, xOffset, yPos);
            yPos += TEXT_LINE_SPACING * _scale;

            graphics.DrawString($"亮度: {roi.SD[0]}", _titleFont, _redBrush, xOffset, yPos);
            yPos += TEXT_LINE_SPACING * _scale;

            graphics.DrawString(roi.SD[1], _normalFont, _lightBlueBrush, 3400 * _scale, 2800 * _scale);
            graphics.DrawString(roi.SD[2], _largeFont, _blueBrush, TEXT_OFFSET_X * _scale, 600 * _scale);

            DrawStatusInfo(graphics, roi);
        }

        private void DrawStatusInfo(Graphics graphics, ROIModel roi)
        {
            int step = roi.ParseInt("ss", 0);
            float yPos = TEXT_OFFSET_Y_BASE * _scale;
            float xOffset = 2600 * _scale;

            if (step == 8 || step == 9)
            {
                graphics.DrawString($"结晶检测: {roi.SD[7]}", _titleFont, _redBrush, xOffset, yPos);
                yPos += TEXT_LINE_SPACING * _scale;
            }

            if (step == 24 || step == 4)
            {
                graphics.DrawString("MLD实时:35.5", _titleFont, _redBrush, xOffset, yPos);
            }
            else
            {
                graphics.DrawString("MLD均值:35.5", _titleFont, _redBrush, xOffset, yPos);
            }
        }

        #endregion

        public void Dispose()
        {
            if (_disposed) return;

            _titleFont?.Dispose();
            _normalFont?.Dispose();
            _largeFont?.Dispose();
            _redBrush?.Dispose();
            _blueBrush?.Dispose();
            _lightBlueBrush?.Dispose();
            _greenPen?.Dispose();
            _redPen?.Dispose();
            _arrowPen?.Dispose();

            _disposed = true;
            GC.SuppressFinalize(this);
        }

        ~PhotoSauceImage()
        {
            Dispose();
        }
    }
}