小弟在一个项目中需要将一批JPEG图片大约2000张,进行处理(局部做上标记)。经过测试处理的过程时间很短,可以忽略不计,但读写文件的时候时间很长。其中处理一张图片需要3.8秒中,给图片加标记只需要120ms 读写JPEG却需要3秒多。显然时间都浪费在i/o上了。想问如何能提高读写文件的速度,也就是 I/O性能。有的人说可以用硬盘缓冲区,有的说用内存映射文件方式写,有点说用多线程的方式写入,现在求教正解。
解决方案 »
- delphi数独
- 求Indy10.5.5的聊天室例程.100分求助
- [DCC Error] Connection.pas(39): E2029 '(' expected but 'THEN' found 错误!!!求救!!!
- 能否设定在dbgrid上某列可以编辑,某列不能编辑?(不是计算列)
- exe如何转换dll的问题 和 如何执行sql命令文件问题
- 如何程序在运行状态,创建一个设计状态的窗体?
- 新手关于面向对象的问题,请各位大侠帮忙!
- 如何实现让一个程序在一运行就自己运行同一目录下的另一程序,并最小化第二个程序
- 关于QReport的简单问题?来者有分!!!
- 如何在调试过程中修改一个长度超过了255的string型变量?
- refresh和query的区别
- fastreport 问题!
file mapping理论上对提高效率能有些帮助,因为理论上操作系统会在适当的时候(一般会比用户更了解应该何时操作)将文件映射到内存和写入硬盘。
多线程没用的,硬盘又不是内存,再多线程也得等硬盘寻道,而且一般也是等到硬盘缓冲区满了之后才写入
2,处理N个.
3,一次连续写入.总体上的时间也许能少点,单个够呛.另一个不知道你的2000个文件是不是都在一个目录,可能是文件过多造成系统遍历文件早成的时间,分成子目录.
要不然硬盘读10M的文件也用不上3秒啊.
2、多线程的话好像不行,同时两个线程进行I/O操作不见得比一个I/O快。
3、我的目录下有5张图片的时候和2000张图片处理时间差不多,所以不是遍历文件耗费了时间
4、每张图片的大小在800k到2M之间。不存在每次读写的字节数少的问题。
5、一次连续读取N个jpeg文件,处理后,一次性写入硬盘。这个方法就是我所期待的。这个应该是建立缓冲的方法。请知道的指出具体实现方法,有关键代码就更好了。或者告诉我关键字,我自己去搜。
Readbmp: TBitmap;
Memorybmp: TBitmap;
ReadJPG: TJpegImage;
writeJPG: TJpegImage;
begin
Readbmp:=TBitmap.Create;
Memorybmp:=TBitmap.Create;
ReadJPG := TJpegImage.Create;
writeJPG := TJpegImage.Create;
ReadJPG.LoadFromFile(PicName);
ReadBmp.Assign(ReadJPG);
ReadBmp.Assign(RightTopPoint(ReadBmp));
writeJPG.Assign(LeftBottomPoint(ReadBmp));
writeJPG.SaveToFile(SavePath+ExtractFileName(PicName));
Readbmp.Free;
Memorybmp.Free;
ReadJPG.Free;
writeJPG.Free;
end;
我的意思是这样的,你先试试:
比如建立5个jpeg文件格式对象(用数组),引用你的代码ReadJPG1,ReadJPG2,ReadJPG3,ReadJPG4,ReadJPG5,
5个writeJPG.
处理
在按循序把5个写入文件.不要读一个处理一个再写入一个.
Memorybmp:=TBitmap.Create;
ReadJPG := TJpegImage.Create;
writeJPG := TJpegImage.Create;
若果不用Assign,如何来做呢
//加载一个JPG
var
Img: TGpBitmap;
begin
Img := TGpBitmap.Create('c:\a.jpg');
Img.GetHBITMAP(0,Bmp.Handle);
Img.Free;
end;//保存一个JPG
var
Img: TGpBitmap;
Clsid: TGUID;
begin
// 利用TImage.Picture.Bitmap的图像句柄Handle和调色板属性Palette建立一个GDI+位图
with Bmp do
Img := TGpBitmap.Create(Handle, Palette);
// 转换为Jpg格式保存
if GetEncoderClsid('image/Jpg', Clsid) then
bmp.Save('c:\a.Jpg', Clsid);
Img.free ;
end;
2.多线程的利用是当另一个线程在处理的时间来读取文件从而提升I/O系统的利用率,如果确实是卡在IO上面,这个作用也不会太大。
3.如果同一个目录下文件过多,很明显文件的定位命中率会降低。
4.如果文件系统(磁盘)碎片过多,导致文件几乎都不是连续存储的,即使是个10兆的文件也不会比零散的多个文件快。
5.完成端口也许能够帮上一点点忙。
ReadBmp.Height := ReadJPG.Height;
ReadBmp.Canvas.Draw(0, 0, ReadJPG);
Readbmp: TBitmap;
Memorybmp: TBitmap;
ReadJPG: TJpegImage;
writeJPG: TJpegImage;
st, t1, t2, t3, t4, t5: integer;
begin
Readbmp:=TBitmap.Create;
Memorybmp:=TBitmap.Create;
ReadJPG := TJpegImage.Create;
writeJPG := TJpegImage.Create;
st := GetTickCount();
ReadJPG.LoadFromFile(PicName); // 费时 t1
t1 := GetTickCount();
ReadBmp.Assign(ReadJPG); // 费时 t2
t2 := GetTickCount();
ReadBmp.Assign(RightTopPoint(ReadBmp)); // 费时 t3
t3 := GetTickCount();
writeJPG.Assign(LeftBottomPoint(ReadBmp)); // 费时 t4
t4 := GetTickCount();
writeJPG.SaveToFile(SavePath+ExtractFileName(PicName)); // 费时 t5
t5 := GetTickCount(); Dec(t5, t4);
Dec(t4, t3);
Dec(t3, t2);
Dec(t2, t1);
Dec(t1, st); Readbmp.Free;
Memorybmp.Free;
ReadJPG.Free;
writeJPG.Free;
end;
31,953,47,813
0,875,62,954
0,859,62,891
0,891,47,1031
0,875,47,828
0,890,63,969
31,859,47,781
16,875,47,953
0,859,63,797
0,890,47,953目前我的主要代码如下://处理函数
function Pnt(ReadBmp:TBitmap):TBitmap;
var
I,J,m,l: Integer;
PRGB: pRGBTriple;
Gray,Rcolor,Gcolor,Bcolor: Byte;
str: string;
min,max:integer;
begin
Rcolor:=GetRValue(form1.pnlColor.Color);
Gcolor:=GetGValue(form1.pnlColor.Color);
Bcolor:=GetBValue(form1.pnlColor.Color);
min:=StrToInt(form1.Edit1.Text);
max:=StrToInt(form1.Edit2.Text);
for l := 0 to form1.ListBox1.Count - 1 do
begin
with arrRecShap[l] do
for I := Y to Y+H do
begin
PRGB := ReadBmp.ScanLine[I];
inc(PRGB,X);
for J := X to X+W do
begin
Gray := (30*PRGB^.rgbtRed+59*PRGB^.rgbtGreen+11*PRGB^.rgbtBlue)div 100;
if (Gray >= min)and(Gray <= max) then
begin
PRGB^.rgbtRed :=Rcolor;
PRGB^.rgbtGreen :=Gcolor;
PRGB^.rgbtBlue :=Bcolor;
end;
Inc(PRGB);
end;
end;
end;
Result := ReadBmp;
end;//主过程
self.getFilePath;///执行完保存对话框开始计时
Readbmp1:=TBitmap.Create;
ReadJPG1 := TJpegImage.Create;
writeJPG1 := TJpegImage.Create;
try
M:=0;
while m<=PicMemo.Items.Count - 1 do
begin
Application.ProcessMessages;
if PicMemo.Items.Count >=2 then
ProgressBar1.Position := Round(m*100/(PicMemo.Items.Count - 1));
PicName1 := Picmemo.Items.Strings[m];
st := GetTickCount();
ReadJPG1.LoadFromFile(PicName1);
t1 := GetTickCount(); ReadBmp1.Assign(ReadJPG1);
t2 := GetTickCount();
writeJPG1.Assign(Pnt(ReadBmp1));
t3 := GetTickCount(); writeJPG1.SaveToFile(SavePath+ExtractFileName(PicName1));
t4 := GetTickCount();
Dec(t4, t3);
Dec(t3, t2);
Dec(t2, t1);
Dec(t1, st);
memo1.Lines.Add(inttostr(t1)+','+inttostr(t2)+','+inttostr(t3)+','+inttostr(t4));
inc(m);
end;
finally
Readbmp1.Free;
ReadJPG1.Free;
writeJPG1.Free;
end;我性能的提高主要是减少了循环过程内的函数的调用。
你所计算的写入应该同时包含了编码的过程。估计换用GDI Plus会好一些。
这句是写入硬盘的
writeJPG1.SaveToFile(SavePath+ExtractFileName(PicName1));
编码的应该是这句吧
writeJPG1.Assign(Pnt(ReadBmp1));gid plus的我正在试
另外完成端口的以及多线程的是什么思路,谁给说下
var
Stream: TStream;
begin
Stream := TFileStream.Create(Filename, fmCreate);
try
SaveToStream(Stream);
finally
Stream.Free;
end;
end;procedure TJPEGImage.SaveToStream(Stream: TStream);
begin
JPEGNeeded;
with FImage.FData do
Stream.Write(Memory^, Size);
end;procedure TJPEGImage.JPEGNeeded;
begin
if FImage.FData = nil then
Compress;
end;
多谢unsigned
31ms 953ms 47ms 813ms
以下同,这下能看懂了吧
0,875,62,954
0,859,62,891
0,891,47,1031
0,875,47,828
0,890,63,969
31,859,47,781
16,875,47,953
0,859,63,797
0,890,47,953 大概我搞错了 ,暴牙兔是另外一个DELPHI高手的id
var
Readbmp: TBitmap;
ReadJPG,writeJPG: TGPBitmap;
picname,str: String;
m,l: Integer;
Date1:TDateTime;
Clsid: TGUID;
begin
//********************************
self.getFilePath;///执行完保存对话框开始计时
Date1 := now;
Readbmp:=TBitmap.Create;
GetEncoderClsid('image/jpeg', Clsid);
try
for m := 0 to PicMemo.Items.Count - 1 do
begin
Application.ProcessMessages;
if PicMemo.Items.Count >=2 then
ProgressBar1.Position := Round(m*100/(PicMemo.Items.Count - 1));
PicName := Picmemo.Items.Strings[m];
//读JPEG
ReadJPG := TGPBitmap.Create(PicName);
//转BMP
ReadBmp.Handle := ReadJPG.getHBITMAP(0);
//处理BMP+转JPG
ReadBmp.Assign(Pnt(ReadBmp));
WriteJPG := TGPBitmap.Create(ReadBmp.Handle,ReadBmp.Palette);
//保存JPEG
WriteJPG.Save(SavePath+ExtractFileName(PicName), Clsid);
ReadJPG.Free;
writeJPG.Free;
end;
finally
Readbmp.Free;
end;
ProgressBar1.Position := 0;
ShowMessage('处理图片完成 用时:'+formatDateTime('hh:mm:ss zzz',now-date1));
4核CPU Q8200和4G内存
现在是每循环一次都要创建一次Tbitmap,求教使用全局变量的方法
TGDIThread = class(TThread)
private
FIOCPQueue: TIOCP ;
protected
procedure Execute; override;
public
constructor Create(CreateSuspended: Boolean; AIOCPQueue: TIOCP);
destructor Destroy; override;
end;var
gCounter: Integer = 0;procedure ProcessImage(tempBMP: TBitmap; const ASourceFile, DestPath: AnsiString);inline;
begin
//这里处理
end;constructor TGDIThread.Create(CreateSuspended: Boolean; AIOCPQueue: TIOCP);
begin
Inherited Create(true);
AIOCPQueue.Attach;
FIOCPQueue := AIOCPQueue;
if Not CreateSuspended then Resume;
end;procedure TGDIThread.Execute;
var
pSourceJpeg, pDestPath: PAnsiString;
lpOverlapped: POverlapped;
tmpBMP: TBitmap;
begin
tmpBMP := TBitmap.Create;
try
while FIOCPQueue.GetQueuedCompletionStatus(DWORD(pSourceJpeg), DWORD(pDestPath), lpOverlapped) do
begin
if DWORD(lpOverlapped) = SHUTDOWN_FLAG then break;
try
ProcessImage(tmpBMP, pSourceJpeg^, pDestPath^);
InterlockedDecrement(gCounter);
finally
DisposeStr(pSourceJpeg);
DisposeStr(pDestPath);
end;
end;
finally
tmpBMP.Free;
Terminate;
end;
end;Destructor TGDIThread.Destroy;
begin
FIOCPQueue.Free;
Inherited;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
I: Integer;
ProcessThread: TGDIThread;
SystemInfo: TSystemInfo;
PicName: AnsiString;
begin
date1 := now;
Button1.Enabled := false; GetSystemInfo(SystemInfo);
for I := 0 to SystemInfo.dwNumberOfProcessors do
begin
ProcessThread := TGDIThread.Create(false, FIOCPQueue);
ProcessThread.FreeOnTerminate := true;
ProcessThread.Resume;
end; for I := 0 to PicMemo.Items.Count - 1 do
begin
PicName := Picmemo.Items.Strings[I];
if PicName = '' then continue;
if FIOCPQueue.PostQueuedCompletionStatus(LongWord(NewStr(PicName)), Longword(NewStr(SavePath)), 0) then InterlockedIncrement(gCounter);
end; Timer1.Interval := 100;
Timer1.Enabled := true;
end;procedure TForm1.FormCreate(Sender: TObject);
begin
FIOCPQueue := TIOCP.Create;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
FIOCPQueue.ShutDownAllEx;
FIOCPQueue.Free;
end;procedure TForm1.Timer1Timer(Sender: TObject);
begin
if gCounter = 0 then begin
Timer1.Enabled := false;
FIOCPQueue.ShutDownAllEx;
ShowMessage('处理图片完成 用时:'+formatDateTime('hh:mm:ss zzz',now-date1));
Button1.Enabled := true;
end;
end;
有三个关键的线程变量
1. 工作状态: 空闲、正在处理
2. 源文件名
3. 目标文件名工作状态为空闲时,程序的主线程给该线程设置源文件名、目标文件名,置工作状态为“正在处理”,结程检测到工作状态为正在处理,就进行图像读取及处理保存,完成后再置线程状态为空闲。
其他的正在试