Is there a way to resize an image using GPU?
Asked Answered
H

3

5

Is there a way to resize an image using GPU (graphic card) that is consumable through a .NET application?

I am looking for an extremely performant way to resize images and have heard that the GPU could do it much quicker than CPU (GDI+ using C#). Are there known implementations or sample code using the GPU to resize images that I could consume in .NET?

Hautbois answered 29/10, 2012 at 22:46 Comment(4)
Anything is faster than GDI+. Perhaps this can help: opencv.org Can be imported with DLLImport in .net.Shrieve
Does this use GPU and just more optimal CPU code?Hautbois
According to the documentation it uses GPU, but only from Nvidia (CUDA). Otherwise CPU. opencv.willowgarage.com/wiki/OpenCV_GPUShrieve
If you're willing to sacrifice quality, WIC is about as fast as it gets. So far, all the attempts I've seen a GPU-based server-side processing have failed to scale as well as the CPU-base onesMarquee
L
7

Have you thought about using XNA to resize your images? Here you can find out how to use XNA to save image as a png/jpeg to a MemoryStream and later reuse it a Bitmap object:

EDIT: I will post an example here (taken from the link above) on how you can possibly use XNA.

public static Image Texture2Image(Texture2D texture)
{
    Image img;
    using (MemoryStream MS = new MemoryStream())
    {
        texture.SaveAsPng(MS, texture.Width, texture.Height);
        //Go To the  beginning of the stream.
        MS.Seek(0, SeekOrigin.Begin);
        //Create the image based on the stream.
        img = Bitmap.FromStream(MS);
    }
    return img;
 }

I also found out today that you can OpenCV to use GPU/multicore CPUs. You can for example choose to use a .NET wrapper such as Emgu and and use its Image class to manipulate with your picture and return a .NET Bitmap class:

public static Bitmap ResizeBitmap(Bitmap sourceBM, int width, int height)
{
    // Initialize Emgu Image object
    Image<Bgr, Byte> img = new Image<Bgr, Byte>(sourceBM); 

    // Resize using liniear interpolation
    img.Resize(width, height, INTER.CV_INTER_LINEAR);

    // Return .NET Bitmap object
    return img.ToBitmap();
}
Lareelareena answered 29/10, 2012 at 23:32 Comment(1)
if you could post a working sample, to avoid link rot that would be helpful.Sexy
G
1

I wrote a quick spike to check performance using WPF, though I cannot for sure say that its using the GPU.

Still, see below. This scales an image to 33.5 (or whatever) times its original size.

public void Resize()
{
    double scaleFactor = 33.5;

    var originalFileStream = System.IO.File.OpenRead(@"D:\SkyDrive\Pictures\Random\Misc\DoIt.jpg");

    var originalBitmapDecoder = JpegBitmapDecoder.Create(originalFileStream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);

    BitmapFrame originalBitmapFrame = originalBitmapDecoder.Frames.First();

    var originalPixelFormat = originalBitmapFrame.Format;

    TransformedBitmap transformedBitmap =
        new TransformedBitmap(originalBitmapFrame, new System.Windows.Media.ScaleTransform()
        {
            ScaleX = scaleFactor,
            ScaleY = scaleFactor
        });

    int stride = ((transformedBitmap.PixelWidth * transformedBitmap.Format.BitsPerPixel) + 7) / 8;
    int pixelCount = (stride * (transformedBitmap.PixelHeight - 1)) + stride;

    byte[] buffer = new byte[pixelCount];

    transformedBitmap.CopyPixels(buffer, stride, 0);

    WriteableBitmap transformedWriteableBitmap = new WriteableBitmap(transformedBitmap.PixelWidth, transformedBitmap.PixelHeight, transformedBitmap.DpiX, transformedBitmap.DpiY, transformedBitmap.Format, transformedBitmap.Palette);

    transformedWriteableBitmap.WritePixels(new Int32Rect(0, 0, transformedBitmap.PixelWidth, transformedBitmap.PixelHeight), buffer, stride, 0);

    BitmapFrame transformedFrame = BitmapFrame.Create(transformedWriteableBitmap);

    var jpegEncoder = new JpegBitmapEncoder();
    jpegEncoder.Frames.Add(transformedFrame);

    using (var outputFileStream = System.IO.File.OpenWrite(@"C:\DATA\Scrap\WPF.jpg"))
    {
        jpegEncoder.Save(outputFileStream);
    }
}

The image I was testing was 495 x 360. It resized it to over 16k x 12k in a couple of seconds, including save out.

It resizes to 1.5x around 165 times a second in a single-core run. On an i7 and the GPU seemingly doing nothing, CPU at 20% I'd expect to get 5x more when multithreaded.

Performance profiling shows a hot path to wpfgfx_v0400.dll which is the native WPF graphics library and is close to DirectX (look-up 'milcore' in Google).

So it might be accelerated, I don't know.

Luke

Girvin answered 20/3, 2014 at 16:43 Comment(0)
G
1

Yes, it is possible to use GPU to resize your images. This can be done using DirectX Surfaces (for example using SlimDx in C#). You should create a surface and move your image to it, and then you can stretch this surface to another target surface of your desired size using only GPU, and finally get back the resized image from the target surface. In these scenario, pixel format of the surfaces can be different and the GPU automatically handles it. But here there are things that can affect the performance of this operation. Moving data between GPU and CPU is a time consuming process. You can apply some techniques to boost performance based on your situation, and avoiding extra data transfer between CPU and GPU memory.

Goober answered 5/4, 2015 at 10:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.