C# 在100毫秒内查看大型多页Tif图像
我正在使用WinForms。在我的表单中有一个C# 在100毫秒内查看大型多页Tif图像,c#,.net,image,winforms,image-resizing,C#,.net,Image,Winforms,Image Resizing,我正在使用WinForms。在我的表单中有一个图片框(设置为正常模式),下一个和上一个按钮。我想快速调整和加载多页TIF图像。当我转到多页TIF图像中的下一页时,每次将图像绘制到pictureBox,我都会遇到延迟。图像的平均速度大约需要800毫秒我希望页面在100毫秒内加载。 我希望处理大型TIF图像的性能与IrfanView一样快。IrfanView是一个小型图像查看应用程序。如果下载IrfanView,您可以看到性能有多快。目前我有另一个解决方案,我使用多线程后台工作程序将TIF页面加载到
图片框
(设置为正常模式
),下一个和上一个按钮。我想快速调整和加载多页TIF图像。当我转到多页TIF图像中的下一页时,每次将图像绘制到pictureBox
,我都会遇到延迟。图像的平均速度大约需要800毫秒我希望页面在100毫秒内加载。
我希望处理大型TIF图像的性能与IrfanView一样快。IrfanView是一个小型图像查看应用程序。如果下载IrfanView,您可以看到性能有多快。目前我有另一个解决方案,我使用多线程后台工作程序将TIF页面加载到一个数组中,然后将其缩小。这种方法最初需要一些时间,但这里的目标是不必等待
有没有办法提高.NET中大型图像的Graphics.DrawImage
性能
g、 绘图图像(img,0,0,宽度,高度)//此行导致延迟“800毫秒,取决于您的计算机”
- 我处理的TIF图像的大小:宽度=16800,高度=10800
- 仅黑白Tif图像
- 位深度=1
- 分辨率单位=2
使用系统;
使用System.Collections.Generic;
使用系统组件模型;
使用系统数据;
使用系统诊断;
使用系统图;
使用System.Linq;
使用系统文本;
使用System.Threading.Tasks;
使用System.Windows.Forms;
名称空间Tif_性能_问题
{
公共部分类Form1:Form
{
int计数器=-1;
int frameCount=0;
秒表s=新秒表();
图像img;
图像[]图像;
公共表格1()
{
初始化组件();
}
私有无效btn\u打开\u单击(对象发送者,事件参数e)
{
var s=新秒表();
s、 Start();
s、 停止();
this.Text=“已用时间毫秒”+s.elapsedmillyses;
img=Image.FromFile(@“C:\Image\Large\u Tif\u Image\u 15pages.Tif”);
frameCount=img.GetFrameCount(System.Drawing.Imaging.FrameDimension.Page);
图像=新图像[帧数];
对于(int i=0;i=帧数)
{
计数器=帧数-1;
btn_Next.Enabled=false;
}
btn_Next.Enabled=false;
LoadPage();
btn_Next.Enabled=true;
}
私有void加载页()
{
StartWatch();
img.选择活动框架(System.Drawing.Imaging.FrameDimension.Page,计数器);
pictureBox1.Image=ResizeImage((Image)img.Clone(),pictureBox1.Width,pictureBox1.Height);
pictureBox1.Refresh();
秒表();
}
公共图像大小图像(图像img、整数宽度、整数高度)
{
位图b=新位图(宽度、高度);
使用(Graphics g=Graphics.FromImage((Image)b))
{
g、 绘图图像(img,0,0,宽度,高度);
}
返回(图像)b;
}
私有void StartWatch()
{
s、 Start();
}
私人秒表
{
s、 停止();
this.Text=“已用时间毫秒:”+s.elapsedmillyses;
s、 重置();
}
}
}
参考资料
IrfanView:
测试:下面的大TIF图像
Visual Studio解决方案
非常昂贵的是图像的大小调整,因为它是一个大图像(在调整大小之前,您还有一个额外的克隆,这似乎没有用,成本约为10%) 我不确定你能不能找到一个更快的加载程序/大小调整程序,也许是irfan view专门写的(t如果与示例中的一样,是一个简单的1 bpp黑白图像。加载图像后,您可以在多线程模式下调整大小,例如生成2、4、8或16个工作线程,每个线程位于图像的矩形部分,并除以总线程数) 不含任何第三方,以下是在您的环境中工作的纯.NET示例,它具有一个特定的多线程SizedTimage实用程序类,可缓存内存中已调整大小的所有帧。运行该实用程序时,您将只看到最初的~1s加载时间,然后浏览图像时不应引起注意:
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;
}
}
公共部分类表单1:表单
{
大小化图像;
私有无效btn\u打开\u单击(对象发送者,事件参数e)
{
...
_tif=新大小的tif图像(@“大tif图像”15页.tif),pictureBox1.宽度,pictureBox1.高度);
pictureBox1.Image=\u tif.GetFrame(0);
btn\u下一步单击(空,空);
}
私有无效btn\u下一步单击(对象发送者,事件参数e)
{
计数器++;
如果(计数器>=\t帧计数)
{
计数器=_tif.FrameCount-1;
btn_Next.Enabled=false;
}
btn_Next.Enabled=false;
LoadPage();
btn_Next.Enabled=true;
}
私有void加载页()
{
StartWatch();
pictureBox1.Image=\u tif.GetFrame(计数器);
秒表();
}
}
公共类大小的Difimage:IDisposable
{
私人形象"形象;;
私有ConcurrentDictionary_frames=新ConcurrentDictionary();
公共大小的Difimage(字符串文件名、整型宽度、整型高度)
{
宽度=宽度;
高度=高度;
_image=image.FromFile(文件名);
FrameCount=\u image.GetFrameCount(FrameDimension.Page);
ThreadPool.QueueUserWorkItem(ResizeFrame);
}
public int FrameCount{get;private set;}
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;
}
}
private override OnPaint(object sender, PaintEventArgs e)
{
e.Graphics.CompositingQuality = CompositingQuality.HighSpeed;
e.Graphics.InterpolationMode = InterpolationMode.NearestNeighbor;
e.Graphics.SmoothingMode = SmoothingMode.None;
e.Graphics.PixelOffsetMode = PixelOffsetMode.Half;
e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
e.Graphics.CompositingMode = CompositingMode.SourceCopy;
base OnPaint(sender, e)
}
public unsafe Image ResizeImage(Bitmap img, int width, int height)
{
var stopwatch = Stopwatch.StartNew();
var imgBits = img.LockBits(new Rectangle(Point.Empty, img.Size), ImageLockMode.ReadOnly, img.PixelFormat);
Bitmap b = new Bitmap(width, height);
var bBits = b.LockBits(new Rectangle(Point.Empty, b.Size), ImageLockMode.WriteOnly, b.PixelFormat);
for (int j = 0; j < height; j++)
{
var imgJ = j * img.Height / height;
for (int i = 0; i < width; i++)
{
var imgI = i * img.Width / width;
var imgPointer = (byte*)imgBits.Scan0 + imgJ * imgBits.Stride + (imgI >> 3);
var mask = (byte)(0x80 >> (imgI & 0x7));
var imgPixel = (uint)(*imgPointer & mask);
var bPointer = (uint*)bBits.Scan0 + j * bBits.Width + i;
*bPointer = imgPixel > 0 ? 0x00FFFFFF : 0xFF000000;
}
}
img.UnlockBits(imgBits);
b.UnlockBits(bBits);
stopwatch.Stop();
Console.WriteLine("Resize to " + width + " x " + height + " within " + stopwatch.ElapsedMilliseconds + "ms");
return b;
}
public void Test()
{
var rawImage = new Bitmap(@"Large_Tif_Image_15pages.tif");
rawImage.SelectActiveFrame(FrameDimension.Page, 3);
pictureBox1.Image = ResizeImage(rawImage, pictureBox1.Width, pictureBox1.Height);
}
var outputFactor = 1.5;
var outputWidth = (int)(pictureBox1.Width * outputFactor);
var outputHeight = (int)(pictureBox1.Height * outputFactor);
var outputImage = ResizeImage(rawImage, outputWidth, outputHeight);