图片转换太慢了啊...有没有更快的方法?目的:将一个较大的图片文件转换成较小的图片显示在PictureBox中。文件一般jpg格式,几MB不等,图片大小为几千x几千,例如8000x4000。要按图片比例缩小,比如缩小到2000x1000,然后显示到PictureBox中。我的方法是先用Image.FromFile方法载入文件,得到原始图片大小W0xH0,然后计算成比例缩小后的大小W1xH1。
再用new Bitmap(Image,W1,H1)的方法得到较小的图片用于显示。现在的问题是new Bitmap的这个操作很费时,一般在数百毫秒甚至超过一秒。有没有更快的方法?另:因为要同时显示多幅图片,直接将文件调入PictureBox再按比例缩小显示的方法很消耗内存,故没有采用。

解决方案 »

  1.   

    不需要转换的,在PictureBox控件中显示图片,PictureBox控件带有这个功能的,只要需要设置PictureBox1.SizeModel=PictureBoxSizeMode.Zoom就可以了
      

  2.   


    我在问题中提到了这个方法,但没有使用。
    虽然PictureBox可以缩小显示大图片,但PictureBox扔保持原尺寸图片的数据,内存消耗很大。如果显示多个图片,会显示内存不足。
    我的做法是先得到一个较小尺寸的图片再给PictureBox显示,这样内存消耗较小。只是得到较小图片的操作很费时间。有没有更好的方法?
      

  3.   

     bp = (Bitmap)bp.GetThumbnailImage(W1,H1, myCallback, IntPtr.Zero)这个会快点吗??
      

  4.   


    我在问题中提到了这个方法,但没有使用。
    虽然PictureBox可以缩小显示大图片,但PictureBox扔保持原尺寸图片的数据,内存消耗很大。如果显示多个图片,会显示内存不足。
    我的做法是先得到一个较小尺寸的图片再给PictureBox显示,这样内存消耗较小。只是得到较小图片的操作很费时间。有没有更好的方法?
    你之所以会显示内存不足,主要是你在切换图片显示的时候没有释放掉上一张图片的资源了,没有释放的话就所有图片都加载在内存中,当然会提示内存不足了,你可以再切换之前调用Image.Dispose方法就没有这样的问题了
      

  5.   

    lizhi3186575 
    Learning_Hard :我不是一次显示一个图片,而是同时显示多个图片。
      

  6.   

    如果都是jpg图片。GetThumbnailImage这个方法是挺不错的。
    不过不能用.net自带的。
    搜一下jpg格式。用jpg格式读出这个挺简单速度也相当快。
    我以前帮别人做的一个排版的系统。读取写真照片就用这个方法。
      

  7.   

    不用.net自带的,用哪个啊???自己去写啊。好象系统也提供了这个API。。
      

  8.   


    客户的图片不只是jpg格式的。
      

  9.   


    从文件读取速度还可以,是读完图片后转换操作(大图转小图)慢。
    如果你用 Graphics 进行 DrawImage的话,不慢才怪。类似如下代码:       Bitmap newImage = new Bitmap(newWidth, newHeight, PixelFormat.Format24bppRgb);
            using (Graphics graphics = Graphics.FromImage(newImage))
            {
                graphics.CompositingQuality = CompositingQuality.HighQuality;
                graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
                graphics.SmoothingMode = SmoothingMode.HighQuality;
                graphics.DrawImage(image, 0, 0, newWidth, newHeight);
            }
    采用这种试试:
    private static byte BytesPerPixel(PixelFormat px)
    {
        switch (px)
        {
            case PixelFormat.Format16bppRgb555:
            case PixelFormat.Format16bppRgb565:
            case PixelFormat.Format16bppGrayScale:
            case PixelFormat.Format16bppArgb1555: return 2;
            case PixelFormat.Format24bppRgb: return 3;
            case PixelFormat.Format32bppRgb: return 4;
            case PixelFormat.Format32bppArgb: return 4;
            case PixelFormat.Format32bppPArgb: return 4;
        }
        return 0;
    }public static Bitmap ThumbnailLow(Bitmap img, Size sz)
    {
        if (img == null || sz.IsEmpty) return null;
        if (sz.Width > img.Width) sz = new Size(img.Width, sz.Height);
        if (sz.Height > img.Height) sz = new Size(sz.Width, img.Height);
        PixelFormat px = img.PixelFormat;
        PixelFormat pxn = PixelFormat.Format16bppRgb565;
        if (px == PixelFormat.Format32bppArgb || px == PixelFormat.Format32bppPArgb ||
            px == PixelFormat.Format32bppRgb) pxn = PixelFormat.Format24bppRgb;
        byte bytesPerPixel = BytesPerPixel(px), bytesPerPixel2 = BytesPerPixel(pxn);
        Bitmap nueva = new Bitmap(sz.Width, sz.Height, pxn);
        BitmapData bmpData = img.LockBits(new Rectangle(0, 0, img.Width, img.Height), ImageLockMode.ReadWrite, px);
        BitmapData bmpData2 = nueva.LockBits(new Rectangle(0, 0, sz.Width, sz.Height), ImageLockMode.ReadWrite, pxn);
        // 快速转换
        float inc_djn = img.Width / (float)sz.Width;
        float inc_din = img.Height / (float)sz.Height;
        float din = 0, djn = 0;
        bool _16bits = bytesPerPixel == 2 || bytesPerPixel2 == 2;
        unsafe
        {
            byte* ptr = (byte*)(bmpData.Scan0);
            byte* ptr2 = (byte*)(bmpData2.Scan0);
            int nOffset = bmpData.Stride - (bmpData.Width * bytesPerPixel);
            int nOffset2 = bmpData2.Stride - (bmpData2.Width * bytesPerPixel2);
            int h = bmpData.Height, w = bmpData.Width;
            for (int i = 0; i < h; i++)
            {
                bool lok = i >= din;
                djn = 0;
                for (int j = 0; j < w; j++)
                {
                    if (lok && j >= djn)
                    {
                        ptr2[0] = ptr[0];
                        ptr2[1] = ptr[1];
                        if (!_16bits) ptr2[2] = ptr[2];
                        ptr2 += bytesPerPixel2;
                        djn += inc_djn;
                    }
                    ptr += bytesPerPixel;
                }
                if (lok) { ptr2 += nOffset2; din += inc_din; }
                ptr += nOffset;
            }
        }
        img.UnlockBits(bmpData);
        nueva.UnlockBits(bmpData2);
        return nueva;
    }
    测试上述方法  sw.Start();
      Image i1 = ThumbnailLow(img, new Size(100, 100)); 
      sw.Stop();//  
      sw2.Start();
      Image i2 = img.GetThumbnailImage(100, 100, null, IntPtr.Zero); 
      sw2.Stop(); // 正常情况,应该ThumbnailLow 比GetThumbnailImage 快一倍
      

  10.   

     sw 和 sw2 是  Stopwatch ,忘了贴上
    Stopwatch sw = new Stopwatch();
     Stopwatch  sw2 = new Stopwatch(); 
    Stopwatch sw = new Stopwatch();
     Stopwatch  sw2 = new Stopwatch(); 
      sw.Start();
      Image i1 = ThumbnailLow(img, new Size(100, 100)); 
      sw.Stop();//  
      sw2.Start();
      Image i2 = img.GetThumbnailImage(100, 100, null, IntPtr.Zero); 
      sw2.Stop(); // 正常情况,应该ThumbnailLow 比GetThumbnailImage 快一倍
      

  11.   


    我在问题中提到了这个方法,但没有使用。
    虽然PictureBox可以缩小显示大图片,但PictureBox扔保持原尺寸图片的数据,内存消耗很大。如果显示多个图片,会显示内存不足。
    我的做法是先得到一个较小尺寸的图片再给PictureBox显示,这样内存消耗较小。只是得到较小图片的操作很费时间。有没有更好的方法?你的方法本身就错了,你这种做法相当于,已取到了大图读到内存,再把大图从内存中变成小图,再显示小图,好比拖了裤子放屁,反而更耗内存。还不如直接显示大图。
    你要想快,就是写个一工具一次性把大图全缩成小图,然后保存到一个目前录。名称就在大图的名称前面加个m_大图名称.jpg这样。然后每次只要读取小图就行了,等要看大图就再去读对应的大图。
      

  12.   


    经过测试,ThumbnailLow方法比img.GetThumbnailImage方法慢一倍时间左右,一个是1500毫秒,一个是780毫秒。
    可能的原因是我的所谓小图并不是如100x100的缩略图。假如大图片是8000x6000,我的小图的意思是适合显示器显示的大小,例如1800x1000。
      

  13.   


    FileStream ImgStream = File.OpenRead(ImgPath);
    Image TempImage = Image.FromStream(ImgStream,false,false);     
     Bitmap Bmp = new Bitmap((int)drawWidth, (int)drawHeight);
                Graphics Myg = Graphics.FromImage(Bmp);
                Myg.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Default;
                Myg.DrawImage(TempImage , drawpoint.X, drawpoint.Y, drawWidth, drawHeight);
                return Bmp;
    大图显示在picturebox中速度慢的主要原因发生在读取图片文件的时候。比如8000*4000的jpg图片文件大小可能达到10m左右,10m左右文件完全读取到内存中已经花费了几百毫秒,甚至秒级了。
    图片缩放所需的时间,其实是非常少的。
    所幸,GDI+类库已经考虑到这这点,可以不完全读取图片文件,就可以做缩放。
    如上面代码所示,几乎是GDI+能达到的最大速度。如果再要快,楼主需要自己解析图片格式和图片缩放。
      

  14.   

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Drawing;
    using System.Drawing.Imaging;
    using System.IO;
    using System.Runtime.InteropServices;
    using System.Text;
    using System.Text.RegularExpressions;
    using System.Threading;
    using System.Windows.Forms;
    using Microsoft.Win32;
    namespace ol
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
            private void button1_Click(object sender, EventArgs e)
            {
                LogTraceInfo("A",true);
                bool zoom;
                pictureBox1.Image = GetZoomedImage(@"F:\x\8\a\1.jpg", 3000, 2000, out zoom);            //1.jpg: 8000x6000, 896KB
                LogTraceInfo("B");
            }        static DateTime _oRefTime = DateTime.Now;        public static void LogTraceInfo(string info, bool reset = false)
            {
                string file = Application.ExecutablePath + ".log";            DateTime now = DateTime.Now;
                if (reset) _oRefTime = now;
                TimeSpan ts = now - _oRefTime;            string text = now.ToString("HH:mm:ss.fff  ") + ts.ToString(@"hh\:mm\:ss\.fff") + "   " + info + Environment.NewLine;            Debug.Print(text);
                //File.AppendAllText(file, text);
            }
            public static Image GetZoomedImage(string asImageFilename, int width, int height, out bool zoomed)
            {
                Image oNewImage = null;            LogTraceInfo("ReadAllBytes",true);
                using (MemoryStream ms = new MemoryStream(File.ReadAllBytes(asImageFilename)))
                {
                    LogTraceInfo("ReadAllBytes--");                LogTraceInfo("FromStream",true);
                    Image oOriginalImage = Image.FromStream(ms, true, false);
                    LogTraceInfo("FromStream--");                double fWScaleRatio = width / (double)oOriginalImage.Width;
                    double fHScaleRatio = height / (double)oOriginalImage.Height;
                    double fScaleRatio = Math.Min(fWScaleRatio, fHScaleRatio);
                    if (width < 0) fScaleRatio = fHScaleRatio;
                    if (height < 0) fScaleRatio = fWScaleRatio;
                    int nNewWidth = (int)(oOriginalImage.Width * fScaleRatio);
                    int nNewHeight = (int)(oOriginalImage.Height * fScaleRatio);                if (fScaleRatio < 1.0)
                    {
                        Size sz = new Size(nNewWidth, nNewHeight);
                        LogTraceInfo("new Bitmap", true);                    //Method 1: need 796,828 ms
                        oNewImage = new Bitmap(oOriginalImage, nNewWidth, nNewHeight);                    //Method 2: need 1546,1578 ms
                        //oNewImage = ThumbnailLow((Bitmap)oOriginalImage, sz);                    //Method 3: need 812,828 ms
                        //oNewImage = ThumbnailAAAA((Bitmap)oOriginalImage, nNewWidth, nNewHeight);                    //Method 4: need 765,875 ms
                        //oNewImage = oOriginalImage.GetThumbnailImage(nNewWidth, nNewHeight, new Image.GetThumbnailImageAbort(ImageThumbnailAbortCallback), IntPtr.Zero);                    LogTraceInfo("Bitmap--");
                        zoomed = true;
                    }
                    else
                    {
                        LogTraceInfo("Clone", true);
                        oNewImage = (Image)oOriginalImage.Clone();
                        LogTraceInfo("Clone--");
                        zoomed = false;
                    }                LogTraceInfo("Dispose", true);
                    oOriginalImage.Dispose();
                    oOriginalImage = null;
                    LogTraceInfo("Dispose--");
                }            return oNewImage;
            }
            private static bool ImageThumbnailAbortCallback()
            {
                return false;
            }        private static byte BytesPerPixel(PixelFormat px)
            {
                switch (px)
                {
                    case PixelFormat.Format16bppRgb555:
                    case PixelFormat.Format16bppRgb565:
                    case PixelFormat.Format16bppGrayScale:
                    case PixelFormat.Format16bppArgb1555: return 2;
                    case PixelFormat.Format24bppRgb: return 3;
                    case PixelFormat.Format32bppRgb: return 4;
                    case PixelFormat.Format32bppArgb: return 4;
                    case PixelFormat.Format32bppPArgb: return 4;
                }
                return 0;
            }        public static Bitmap ThumbnailLow(Bitmap img, Size sz)
            {
                if (img == null || sz.IsEmpty) return null;
                if (sz.Width > img.Width) sz = new Size(img.Width, sz.Height);
                if (sz.Height > img.Height) sz = new Size(sz.Width, img.Height);
                PixelFormat px = img.PixelFormat;
                PixelFormat pxn = PixelFormat.Format16bppRgb565;
                if (px == PixelFormat.Format32bppArgb || px == PixelFormat.Format32bppPArgb ||
                    px == PixelFormat.Format32bppRgb) pxn = PixelFormat.Format24bppRgb;
                byte bytesPerPixel = BytesPerPixel(px), bytesPerPixel2 = BytesPerPixel(pxn);
                Bitmap nueva = new Bitmap(sz.Width, sz.Height, pxn);
                BitmapData bmpData = img.LockBits(new Rectangle(0, 0, img.Width, img.Height), ImageLockMode.ReadWrite, px);
                BitmapData bmpData2 = nueva.LockBits(new Rectangle(0, 0, sz.Width, sz.Height), ImageLockMode.ReadWrite, pxn);
                // 快速转换 
                float inc_djn = img.Width / (float)sz.Width;
                float inc_din = img.Height / (float)sz.Height;
                float din = 0, djn = 0;
                bool _16bits = bytesPerPixel == 2 || bytesPerPixel2 == 2;
                unsafe
                {
                    byte* ptr = (byte*)(bmpData.Scan0);
                    byte* ptr2 = (byte*)(bmpData2.Scan0);
                    int nOffset = bmpData.Stride - (bmpData.Width * bytesPerPixel);
                    int nOffset2 = bmpData2.Stride - (bmpData2.Width * bytesPerPixel2);
                    int h = bmpData.Height, w = bmpData.Width;
                    for (int i = 0; i < h; i++)
                    {
                        bool lok = i >= din;
                        djn = 0;
                        for (int j = 0; j < w; j++)
                        {
                            if (lok && j >= djn)
                            {
                                ptr2[0] = ptr[0];
                                ptr2[1] = ptr[1];
                                if (!_16bits) ptr2[2] = ptr[2];
                                ptr2 += bytesPerPixel2;
                                djn += inc_djn;
                            }
                            ptr += bytesPerPixel;
                        }
                        if (lok) { ptr2 += nOffset2; din += inc_din; }
                        ptr += nOffset;
                    }
                }
                img.UnlockBits(bmpData);
                nueva.UnlockBits(bmpData2);
                return nueva;
            }        public static Bitmap ThumbnailAAAA(Bitmap img, int ww, int hh)
            {
                Bitmap Bmp = new Bitmap((int)ww, (int)hh); 
                Graphics Myg = Graphics.FromImage(Bmp); 
                Myg.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Default; 
                Myg.DrawImage(img, 0, 0, ww, hh); 
                return Bmp;
            }
        }
    }
    以上是我的测试代码。1.jpg是一张8000x6000的图片,得到一个不大于3000x2000的图片,显示在PictureBox中。
    共测试了4种转换方法,测试两遍,耗时见注释部分。方法1,3,4耗时800毫秒左右,方法2耗时1500毫秒左右。不包括读取文件的耗时。我的目的就是需要转换尽量快的方法。
      

  15.   

    那所以还是 GetThumbnailImage 最快!  楼主给分吧,大神的GDI+都出来了,还是没这个快,哈哈
    要不你就文件流,FileStream  自己夸像素取点构成新缩略图吧,估计没有经过平均插值会很难看
      

  16.   

    jpg文件格式本来就比较大,为什么不用png格式?
      

  17.   

    全图加载在缩放的主要耗时在JPG图像文件的解码,比如我加载一副8000*4000JPG文件,就耗时了700ms左右,而用线性插值缩放到2000*1000  120ms就可以了。一般大图的JPG都在内部缓存了缩略图,但是缩略图的大小一般只有160*120大小左右,所以GDI+ 的GetThumbnailImage 并不是直接读取这个数据。 
      

  18.   

    仔细的研究了下.net下的GetThumbnailImage函数,结果证明着东西对楼主的要求来说不合适.http://www.cnblogs.com/Imageshop/archive/2013/06/16/3138623.html
      

  19.   


    测试过用GetThumbnailImage来获得缩小的图片(比160x120要大很多),显示质量还可以,就是不够快。我的目标就是,不管什么方法,要足够快。