簡體   English   中英

在 C# 中使用 SharpDX 硬件加速縮小圖像的快速方法是什么?

[英]What is a quick way to scale down an image using SharpDX hardware acceleration in C#?

我正在嘗試縮小大圖像(〜23k x 1k)以顯示在winforms中。 我當前縮放圖像的方式花費了太長時間,這就是為什么我想通過 SharpDX (C#) 使用 GPU 來提高性能。 什么是這樣做的好方法?

我正在研究一種通過應用縮放效果來縮放圖像的方法(我現在無法訪問),但我仍然不完全了解 SharpDX,所以我想知道是否有更好的方法來去吧。 我從這個例子中模擬了我的代碼,但我刪除了文本覆蓋、圖像保存、繪圖部分,並用縮放效果替換了高斯。 由於為了簡單起見我使用 GDI 進行繪圖,因此圖像采用系統繪圖位圖的形式,因此我使用內存流初始化編碼器,用於在應用縮放效果后獲取輸出圖像。 我用這種方法做的較小的測試似乎並沒有使縮放更快,但我還不能完全付諸實踐。

有沒有更快的方法來使用 SharpDX 縮小圖像,或者按照我目前的方法最快的方法?

根據我在https://csharp.hotexamples.com/examples/SharpDX.WIC/WICStream/-/php-wicstream-class-examples.html上找到的內容

看起來 SharpDX 的性能大約是 GDI 的兩倍或更好。

在我的 Windows 11 計算機上運行的測試代碼。 即使您像我一樣對 SharpDX 知之甚少,也應該足以讓您入門。

var inputPath = @"x:\Temp\1\_Landscape.jpg";
var data = File.ReadAllBytes(inputPath);

var sw = Stopwatch.StartNew();
var iu6 = new ImageUtilities6();
Debug.WriteLine($"Init: {sw.ElapsedMilliseconds}ms total");

for (int i = 0; i < 10; i++)
{
    sw.Restart();
    var image = iu6.ResizeImage(data, 799, 399);
    Debug.WriteLine($"Resize: {sw.ElapsedMilliseconds}ms total");

    File.WriteAllBytes(@"X:\TEMP\1\007-xxx.jpg", image);
}

sw.Restart();
iu6.Dispose();
Debug.WriteLine($"Dispose: {sw.ElapsedMilliseconds}ms total");

我根據該頁面上的示例制作的課程。

using SharpDX;
using dw = SharpDX.DirectWrite;
using d2 = SharpDX.Direct2D1;
using d3d = SharpDX.Direct3D11;
using dxgi = SharpDX.DXGI;
using wic = SharpDX.WIC;
using System;
using System.IO;
using SharpDX.Direct3D11;
using SharpDX.WIC;
using SharpDX.DirectWrite;

namespace SharpDX_ImageResizingTest
{
    public class ImageUtilities6 : IDisposable
    {
        private Device defaultDevice;
        private Device1 d3dDevice;
        private dxgi.Device dxgiDevice;
        private d2.Device d2dDevice;
        private ImagingFactory2 imagingFactory;
        //private d2.DeviceContext d2dContext;
        private Factory dwFactory;
        private d2.PixelFormat d2PixelFormat;

        public ImageUtilities6()
        {
            //SharpDX.Configuration.EnableObjectTracking = true; //Turn on memory leak logging

            // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            // INITIALIZATION ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

            // initialize the D3D device which will allow to render to image any graphics - 3D or 2D
            defaultDevice = new SharpDX.Direct3D11.Device(SharpDX.Direct3D.DriverType.Hardware,
                                                              d3d.DeviceCreationFlags.VideoSupport
                                                              | d3d.DeviceCreationFlags.BgraSupport
                                                              | d3d.DeviceCreationFlags.Debug); // take out the Debug flag for better performance

            d3dDevice = defaultDevice.QueryInterface<d3d.Device1>(); // get a reference to the Direct3D 11.1 device
            dxgiDevice = d3dDevice.QueryInterface<dxgi.Device>(); // get a reference to DXGI device
            //var dxgiSurface = d3dDevice.QueryInterface<dxgi.Surface>(); // get a reference to DXGI surface

            d2dDevice = new d2.Device(dxgiDevice); // initialize the D2D device

            imagingFactory = new wic.ImagingFactory2(); // initialize the WIC factory


            dwFactory = new dw.Factory();

            // specify a pixel format that is supported by both D2D and WIC
            d2PixelFormat = new d2.PixelFormat(dxgi.Format.R8G8B8A8_UNorm, d2.AlphaMode.Premultiplied);
            // if in D2D was specified an R-G-B-A format - use the same for wic
        }

        public byte[] ResizeImage(byte[] image, int targetWidth, int targetHeight)
        {
            int dpi = 72; //96? does it even matter

            var wicPixelFormat = wic.PixelFormat.Format32bppPRGBA;

            // initialize the DeviceContext - it will be the D2D render target and will allow all rendering operations
            var d2dContext = new d2.DeviceContext(d2dDevice, d2.DeviceContextOptions.None);


            // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            // IMAGE LOADING ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            var imageStream = new MemoryStream(image);
            //var decoder = new wic.PngBitmapDecoder(imagingFactory); // we will load a PNG image
            var decoder = new wic.JpegBitmapDecoder(imagingFactory); // we will load a JPG image
            var inputStream = new wic.WICStream(imagingFactory, imageStream); // open the image for reading
            decoder.Initialize(inputStream, wic.DecodeOptions.CacheOnLoad);

            // decode the loaded image to a format that can be consumed by D2D
            var formatConverter = new wic.FormatConverter(imagingFactory);
            var frame = decoder.GetFrame(0);
            formatConverter.Initialize(frame, wicPixelFormat);

            // load the base image into a D2D Bitmap
            var inputBitmap = d2.Bitmap1.FromWicBitmap(d2dContext, formatConverter, new d2.BitmapProperties1(d2PixelFormat));

            // store the image size - output will be of the same size
            var inputImageSize = formatConverter.Size;
            var pixelWidth = inputImageSize.Width;
            var pixelHeight = inputImageSize.Height;


            // Calculate correct aspect ratio
            double aspectRatio = (double)pixelHeight / (double)pixelWidth;
            double targetAspectRatio = (double)targetHeight / (double)targetWidth;
            if (targetAspectRatio > aspectRatio)
            {
                targetHeight = (int)(targetHeight * (aspectRatio / targetAspectRatio));
            }
            else
            {
                targetWidth = (int)(targetWidth * (targetAspectRatio / aspectRatio));
            }

            // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            // EFFECT SETUP ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

            //Effect 1 : BitmapSource - take decoded image data and get a BitmapSource from it
            //var bitmapSourceEffect = new d2.Effects.BitmapSource(d2dContext);
            //bitmapSourceEffect.WicBitmapSource = formatConverter;

            // Effect 2 : GaussianBlur - give the bitmapsource a gaussian blurred effect
            //var gaussianBlurEffect = new d2.Effects.GaussianBlur(d2dContext);
            //gaussianBlurEffect.SetInput(0, bitmapSourceEffect.Output, true);
            //gaussianBlurEffect.StandardDeviation = 5f;

            // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            // RENDER TARGET SETUP ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

            // create the d2d bitmap description using default flags (from SharpDX samples) and 96 DPI
            var d2dBitmapProps = new d2.BitmapProperties1(d2PixelFormat, 96, 96, d2.BitmapOptions.Target | d2.BitmapOptions.CannotDraw);

            // the render target
            var d2dRenderTarget = new d2.Bitmap1(d2dContext, new Size2(targetWidth, targetHeight), d2dBitmapProps);
            d2dContext.Target = d2dRenderTarget; // associate bitmap with the d2d context

            d2dContext.BeginDraw();

            //d2dContext.DrawImage(bitmapSourceEffect); //Way #1
            //d2dContext.DrawImage(gaussianBlurEffect); //Way #2
            //d2dContext.DrawBitmap(inputBitmap, 1, d2.InterpolationMode.Linear); //Way #3
            d2dContext.DrawBitmap(inputBitmap, new SharpDX.Mathematics.Interop.RawRectangleF(0, 0, targetWidth, targetHeight), 1, d2.InterpolationMode.Linear, null, null); //Way #4 - resizing

            d2dContext.EndDraw();


            // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            // IMAGE SAVING ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

            // delete the output file if it already exists
            //if (System.IO.File.Exists(outputPath)) System.IO.File.Delete(outputPath);

            // use the appropiate overload to write either to stream or to a file
            var outputStream = new MemoryStream();
            var stream = new wic.WICStream(imagingFactory, outputStream);

            // select the image encoding format HERE
            var encoder = new wic.JpegBitmapEncoder(imagingFactory);
            encoder.Initialize(stream);

            var bitmapFrameEncode = new wic.BitmapFrameEncode(encoder);
            bitmapFrameEncode.Options.ImageQuality = 0.95f;
            bitmapFrameEncode.Initialize();
            bitmapFrameEncode.SetSize(targetWidth, targetHeight);
            bitmapFrameEncode.SetPixelFormat(ref wicPixelFormat);

            // this is the trick to write D2D1 bitmap to WIC
            var imageEncoder = new wic.ImageEncoder(imagingFactory, d2dDevice);
            imageEncoder.WriteFrame(d2dRenderTarget, bitmapFrameEncode, new wic.ImageParameters(d2PixelFormat, dpi, dpi, 0, 0, targetWidth, targetHeight));

            bitmapFrameEncode.Commit();
            encoder.Commit();

            imageEncoder.Dispose();

            bitmapFrameEncode.Dispose();
            encoder.Dispose();
            stream.Dispose();
            formatConverter.Dispose();
            d2dRenderTarget.Dispose();
            inputStream.Dispose();
            decoder.Dispose();
            inputBitmap.Dispose();
            frame.Dispose();
            d2dContext.Dispose();

            return outputStream.ToArray();
        }

        public void Dispose()
        {
            //bitmapSourceEffect.Dispose();

            dwFactory.Dispose();
            imagingFactory.Dispose();
            d2dDevice.Dispose();
            dxgiDevice.Dispose();
            d3dDevice.Dispose();
            defaultDevice.Dispose();

            //System.Diagnostics.Debug.WriteLine(SharpDX.Diagnostics.ObjectTracker.ReportActiveObjects()); Log that memory leak
        }


        public byte[] ResizeImage1(byte[] data, int width, int height)
        {
            var ms = new MemoryStream(data);
            //Image image = Image.FromStream(ms);
            System.Drawing.Image image = System.Drawing.Image.FromStream(ms, false, false);

            System.Drawing.Bitmap result = new System.Drawing.Bitmap(width, height);
            // set the resolutions the same to avoid cropping due to resolution differences
            result.SetResolution(image.HorizontalResolution, image.VerticalResolution);

            //use a graphics object to draw the resized image into the bitmap
            using (System.Drawing.Graphics graphics = System.Drawing.Graphics.FromImage(result))
            {
                //set the resize quality modes to high quality
                graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
                graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
                graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
                //draw the image into the target bitmap
                graphics.DrawImage(image, 0, 0, result.Width, result.Height);
            }

            var stream = new System.IO.MemoryStream();
            image.Save(stream, System.Drawing.Imaging.ImageFormat.Jpeg);
            stream.Position = 0;
            return stream.ToArray();
        }
    }
}

使用的庫是 SharpDX + .Direct2D1 + .Direct3D11 + .DXGI 4.2。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM