Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/288.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在100毫秒内查看大型多页Tif图像_C#_.net_Image_Winforms_Image Resizing - Fatal编程技术网

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

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页面加载到

我正在使用WinForms。在我的表单中有一个
图片框
(设置为
正常模式
),下一个和上一个按钮。我想快速调整和加载多页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);