快+ C# 中的高质量图像调整算法,无需使用 GDI+ /WPF

发布于 2024-12-01 16:26:49 字数 155 浏览 2 评论 0原文

我需要调整大量图像的大小而不损失太多质量。
使用PresentationFramework (WPF) 速度相当快,但质量很差。
使用GDI+,质量很好,但锁定到UI-Thread ->单核。 :-(
是否有一种良好且快速的算法将速度和质量结合在一段代码中? ;-)

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

行雁书 2024-12-08 16:26:49

GDI+(又名 System.Drawing)不需要消息泵,并且可以在多个线程上正常工作;只有真正的控件才可以 - 您甚至可以在控制台应用程序(没有消息泵)中使用位图。

因此,例如,您可以执行以下操作(我已经对此进行了测试):

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

编辑:根据这篇文章 似乎 System.Drawing 有一个全局 锁;特别是使用 DrawImage() (尽管请记住,这不是因为消息泵)。不幸的是,获得多线程访问的唯一方法可能是使用 DirectX/SharpDX 并创建设备启用多线程;创建一些后备缓冲区并在那里调整大小 - 然后您可以调整缩小/最大化过滤器。我不确定您是否可以从 DirectX 中的多个线程中 Draw() - 但即使不能,您也应该能够从中获得更多的吞吐量(即使在单个线程上)。

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).

瀟灑尐姊 2024-12-08 16:26:49

没有理由不在一个单独的线程上为此打开一个单独的消息泵,这仅仅意味着您可以在 100 个线程中并行执行此操作;)。

我知道这是一个更神秘的部分,但是有些应用程序通过在每个启动的线程上实现特定的 Windows 消息泵来为每个窗口提供一个独立的线程。为此可以使用相同的方法。就当是.NET UI编程201级吧。

There is no reason not to open a separate message pump for that, on a separate thread, which simply means you can do that in 100 threads in parallel ;).

I know this is a more arcane part, but there are applications that have a sparate thread per windows by implemenmting specific windows message pumps on every thread started. The same approach can be used for that. Take it as .NET UI programming 201 level.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文