在100毫秒内查看大型多页Tif图像

taj*_*i01 11 .net c# image winforms image-resizing

我正在使用WinForms.在我的表单中,我有一个pictureBox(设置为normal mode),下一个和上一个按钮.我想快速调整大小和加载多页TIF图像.当我转到多页TIF图像的下一页时,每次将图像绘制到图像时都会遇到延迟pictureBox.图像的平均速度大约需要800毫秒.我希望页面在100毫秒内加载.

我希望像IrfanView一样快速处理大型TIF图像.IrfanView是一个小型图像查看应用程序.如果您下载IrfanView,您可以看到性能有多快.目前我有另一个解决方案,我使用多线程后台工作程序将TIF页面加载到一个数组然后我缩小它.这种方法最初需要一些时间,但这里的目标是不必等待.

有没有办法提高Graphics.DrawImage.NET中大图像的性能?

g.DrawImage(img,0,0,width,height); //此行导致延迟"800毫秒,具体取决于您的计算机"

  • 我使用的TIF图像的大小:宽度= 16800,高度= 10800
  • 只有黑白Tif图像
  • 位深度= 1
  • 分辨率单位= 2

在此输入图像描述

 using System;
 using System.Collections.Generic;
 using System.ComponentModel;
 using System.Data;
 using System.Diagnostics;
 using System.Drawing;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 using System.Windows.Forms;

namespace Tif_Preformance_Question
{
public partial class Form1 : Form
{

    int counter = -1;
    int frameCount = 0;
    Stopwatch s = new Stopwatch();
    Image img;
    Image[] images;

    public Form1()
    {
        InitializeComponent();
    }

    private void btn_Open_Click(object sender, EventArgs e)
    {
        var s = new Stopwatch();
        s.Start();
        s.Stop();
        this.Text = "Elapsed Time Milliseconds" + s.ElapsedMilliseconds;


        img = Image.FromFile(@"C:\image\Large_Tif_Image_15pages.tif");
        frameCount = img.GetFrameCount(System.Drawing.Imaging.FrameDimension.Page);
        images = new Image[frameCount];

        for (int i = 0; i < frameCount; i++)
        {
            img.SelectActiveFrame(System.Drawing.Imaging.FrameDimension.Page, i);
            images[i] = (Image)img.Clone();
        }
        img.SelectActiveFrame(System.Drawing.Imaging.FrameDimension.Page, 0);
        pictureBox1.Image = (Image)img.Clone();

    }

    private void btn_Next_Click(object sender, EventArgs e)
    {
        counter++;
        if (counter >= frameCount)
        {
            counter = frameCount - 1;
            btn_Next.Enabled = false;
        }
        btn_Next.Enabled = false;
        LoadPage();
        btn_Next.Enabled = true;
    }

    private void LoadPage()
    {

        StartWatch();
        img.SelectActiveFrame(System.Drawing.Imaging.FrameDimension.Page, counter);
        pictureBox1.Image = ResizeImage((Image)img.Clone(), pictureBox1.Width, pictureBox1.Height);
        pictureBox1.Refresh();
        Stopwatch();
    }

    public Image ResizeImage(Image img, int width, int height)
    {
        Bitmap b = new Bitmap(width, height);
        using (Graphics g = Graphics.FromImage((Image)b))
        {
            g.DrawImage(img, 0, 0, width, height);
        }
        return (Image)b;
    }

    private void StartWatch()
    {
        s.Start();
    }
    private void Stopwatch()
    {

        s.Stop();
        this.Text = "Elapsed Time Milliseconds: " + s.ElapsedMilliseconds;
        s.Reset();
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

参考

IrfanView的:

http://www.irfanview.com/

测试:大TIF图像下面

http://www.filedropper.com/largetifimage15pages_2

Visual Studio解决方案

http://www.filedropper.com/tifpreformancequestion_1

Sim*_*ier 3

非常昂贵的是调整图像大小,因为它是一个大图像(在调整大小之前您还有一个额外的克隆,这似乎无用且成本约为 10%)。

我不确定你能找到更快的加载器/调整器,也许 irfan view 专门写了一个(TIF 就像你的示例中的那样是一个简单的 1 bpp 黑白图像。加载图像后,你可以在多线程模式下调整大小,生成例如 2、4、8 或 16 个工作线程,每个工作线程位于图像的一个矩形部分上,然后除以线程数)。

没有任何第 3 方,这里是纯 .NET 示例,可在您的环境中工作,具有特定的多线程 SizedTifImage 实用程序类,该实用程序类可缓存已在内存中调整大小的所有帧。当你运行它时,你只会看到最初的约 1 秒加载时间,然后浏览图像应该不会被注意到:

public partial class Form1 : Form
{
    SizedTifImage _tif;

    private void btn_Open_Click(object sender, EventArgs e)
    {
       ...
        _tif = new SizedTifImage(@"Large_Tif_Image_15pages.tif", pictureBox1.Width, pictureBox1.Height);
        pictureBox1.Image = _tif.GetFrame(0);
        btn_Next_Click(null, null);
    }

    private void btn_Next_Click(object sender, EventArgs e)
    {
        counter++;
        if (counter >= _tif.FrameCount)
        {
            counter = _tif.FrameCount - 1;
            btn_Next.Enabled = false;
        }
        btn_Next.Enabled = false;
        LoadPage();
        btn_Next.Enabled = true;
    }

    private void LoadPage()
    {
        StartWatch();
        pictureBox1.Image = _tif.GetFrame(counter);
        Stopwatch();
    }
}

public class SizedTifImage : IDisposable
{
    private Image _image;
    private ConcurrentDictionary<int, Image> _frames = new ConcurrentDictionary<int, Image>();

    public SizedTifImage(string filename, int width, int height)
    {
        Width = width;
        Height = height;
        _image = Image.FromFile(filename);
        FrameCount = _image.GetFrameCount(FrameDimension.Page);
        ThreadPool.QueueUserWorkItem(ResizeFrame);
    }

    public int FrameCount { get; private set; }
    public int Width { get; private set; }
    public int Height { get; private set; }

    private void ResizeFrame(object state)
    {
        for (int i = 0; i < FrameCount; i++)
        {
            if (_image == null)
                return;

            _image.SelectActiveFrame(FrameDimension.Page, i);
            var bmp = new Bitmap(Width, Height);
            using (var g = Graphics.FromImage(bmp))
            {
                if (_image == null)
                    return;

                g.DrawImage(_image, 0, 0, bmp.Width, bmp.Height);
            }
            _frames.AddOrUpdate(i, bmp, (k, oldValue) => { bmp.Dispose(); return oldValue; });
        }
    }

    public Image GetFrame(int i)
    {
        if (i >= FrameCount)
            throw new IndexOutOfRangeException();

        if (_image == null)
            throw new ObjectDisposedException("Image");

        Image img;
        do
        {
            if (_frames.TryGetValue(i, out img))
                return img;

            Thread.Sleep(10);
        }
        while (true);
    }

    public void Dispose()
    {
        var images = _frames.Values.ToArray();
        _frames.Clear();
        foreach (var img in images)
        {
            img.Dispose();
        }

        if (_image != null)
        {
            _image.Dispose();
            _image = null;
        }
    }
Run Code Online (Sandbox Code Playgroud)