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毫秒,具体取决于您的计算机"
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的:
测试:大TIF图像下面
http://www.filedropper.com/largetifimage15pages_2
Visual Studio解决方案
非常昂贵的是调整图像大小,因为它是一个大图像(在调整大小之前您还有一个额外的克隆,这似乎无用且成本约为 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)