Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fast + good Quality Image Resizing Algorithm in C# without using GDI+ / WPF

I need to resize a lot of images without losing to much quality.
Using PresentationFramework (WPF) is pretty fast, but quality is poor.
Using GDI+, it's good quality, but locked to UI-Thread -> singleCored. :-(
Is there a good and fast algorithm combining speed and quality in one piece of code? ;-)

like image 766
Sascha Avatar asked Oct 20 '25 14:10

Sascha


1 Answers

GDI+ (a.k.a. System.Drawing) doesn't need the message pump and will happily work on multiple threads; only real controls do - you can even use Bitmap in a console application (which doesn't have a message pump).

Thus, for instance, you could do the following (I have tested this):

static void Main(string[] args)
{
    foreach (var file in Directory.GetFiles("C:\\MyImages", "*.jpg"))
    {
        // Spawn threads.
        new Action<string, float>(ResizeImage).BeginInvoke(file, 0.1f, null, null);
    }
    Console.ReadLine();
}

public static void ResizeImage(string filename, float scale)
{
    using (var bitmap = Image.FromFile(filename))
    using (var resized = ResizeBitmap(bitmap, 0.1f, InterpolationMode.HighQualityBicubic))
    {
        var newFile = Path.ChangeExtension(filename, ".thumbnail" + Path.GetExtension(filename));
        if (File.Exists(newFile))
            File.Delete(newFile);
        resized.Save(newFile);
    }
}

public static Bitmap ResizeBitmap(Image source, float scale, InterpolationMode quality)
{
    if (source == null)
        throw new ArgumentNullException("source");

    // Figure out the new size.
    var width = (int)(source.Width * scale);
    var height = (int)(source.Height * scale);

    // Create the new bitmap.
    // Note that Bitmap has a resize constructor, but you can't control the quality.
    var bmp = new Bitmap(width, height);

    using (var g = Graphics.FromImage(bmp))
    {
        g.InterpolationMode = quality;
        g.DrawImage(source, new Rectangle(0, 0, width, height));
        g.Save();
    }

    return bmp;
}

Edit: According to this post it seems as though System.Drawing has a global lock; especially with DrawImage() (although keep in mind, this is not because of the message pump). Unfortunately the only way to get multithreaded access would probably be to use DirectX/SharpDX and create the device with multithreading enabled; create some backbuffers and do the resizing there - you can then tweak the minification/maxification filters. I am not sure if you can Draw() from multiple threads in DirectX - but even if you can't you should be able to get way more throughput from it (even on a single thread).

like image 54
Jonathan Dickinson Avatar answered Oct 23 '25 05:10

Jonathan Dickinson



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!