图片转换太慢了啊...有没有更快的方法?目的:将一个较大的图片文件转换成较小的图片显示在PictureBox中。文件一般jpg格式,几MB不等,图片大小为几千x几千,例如8000x4000。要按图片比例缩小,比如缩小到2000x1000,然后显示到PictureBox中。我的方法是先用Image.FromFile方法载入文件,得到原始图片大小W0xH0,然后计算成比例缩小后的大小W1xH1。
再用new Bitmap(Image,W1,H1)的方法得到较小的图片用于显示。现在的问题是new Bitmap的这个操作很费时,一般在数百毫秒甚至超过一秒。有没有更快的方法?另:因为要同时显示多幅图片,直接将文件调入PictureBox再按比例缩小显示的方法很消耗内存,故没有采用。
再用new Bitmap(Image,W1,H1)的方法得到较小的图片用于显示。现在的问题是new Bitmap的这个操作很费时,一般在数百毫秒甚至超过一秒。有没有更快的方法?另:因为要同时显示多幅图片,直接将文件调入PictureBox再按比例缩小显示的方法很消耗内存,故没有采用。
我在问题中提到了这个方法,但没有使用。
虽然PictureBox可以缩小显示大图片,但PictureBox扔保持原尺寸图片的数据,内存消耗很大。如果显示多个图片,会显示内存不足。
我的做法是先得到一个较小尺寸的图片再给PictureBox显示,这样内存消耗较小。只是得到较小图片的操作很费时间。有没有更好的方法?
我在问题中提到了这个方法,但没有使用。
虽然PictureBox可以缩小显示大图片,但PictureBox扔保持原尺寸图片的数据,内存消耗很大。如果显示多个图片,会显示内存不足。
我的做法是先得到一个较小尺寸的图片再给PictureBox显示,这样内存消耗较小。只是得到较小图片的操作很费时间。有没有更好的方法?
你之所以会显示内存不足,主要是你在切换图片显示的时候没有释放掉上一张图片的资源了,没有释放的话就所有图片都加载在内存中,当然会提示内存不足了,你可以再切换之前调用Image.Dispose方法就没有这样的问题了
Learning_Hard :我不是一次显示一个图片,而是同时显示多个图片。
不过不能用.net自带的。
搜一下jpg格式。用jpg格式读出这个挺简单速度也相当快。
我以前帮别人做的一个排版的系统。读取写真照片就用这个方法。
客户的图片不只是jpg格式的。
从文件读取速度还可以,是读完图片后转换操作(大图转小图)慢。
如果你用 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 快一倍
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 快一倍
我在问题中提到了这个方法,但没有使用。
虽然PictureBox可以缩小显示大图片,但PictureBox扔保持原尺寸图片的数据,内存消耗很大。如果显示多个图片,会显示内存不足。
我的做法是先得到一个较小尺寸的图片再给PictureBox显示,这样内存消耗较小。只是得到较小图片的操作很费时间。有没有更好的方法?你的方法本身就错了,你这种做法相当于,已取到了大图读到内存,再把大图从内存中变成小图,再显示小图,好比拖了裤子放屁,反而更耗内存。还不如直接显示大图。
你要想快,就是写个一工具一次性把大图全缩成小图,然后保存到一个目前录。名称就在大图的名称前面加个m_大图名称.jpg这样。然后每次只要读取小图就行了,等要看大图就再去读对应的大图。
经过测试,ThumbnailLow方法比img.GetThumbnailImage方法慢一倍时间左右,一个是1500毫秒,一个是780毫秒。
可能的原因是我的所谓小图并不是如100x100的缩略图。假如大图片是8000x6000,我的小图的意思是适合显示器显示的大小,例如1800x1000。
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+能达到的最大速度。如果再要快,楼主需要自己解析图片格式和图片缩放。
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毫秒左右。不包括读取文件的耗时。我的目的就是需要转换尽量快的方法。
要不你就文件流,FileStream 自己夸像素取点构成新缩略图吧,估计没有经过平均插值会很难看
测试过用GetThumbnailImage来获得缩小的图片(比160x120要大很多),显示质量还可以,就是不够快。我的目标就是,不管什么方法,要足够快。