各位大侠,我编了个图像采集的程序,图像卡用的是大恒CG300。图像采集后进行了二值处理。画面闪烁不停,而且运行4到5分钟后,就出来了一个错误提示框:Project Project1.exe raised exception class EOutOfResources with message 'Out of system resource' .Process stopped or Run to continue.这个问题已经困扰我三天了,还是找不到原因,我觉得我把资源都及时释放了啊。请各位大侠前辈抽空帮我看看到底问题出在哪里,非常谢谢您了!!!btw:特别想请大家帮我看看function TForm1.Draw这个函数,我觉得这里出问题的可能性较大。程序如下:(由于论坛里不能贴太长的贴子,所以前面的接口声明部分及一些变量声明我就没贴出来了)
//用户创建的一帧图像数据缓冲区大小,用来将图像卡采集到内存的数据读出到该缓冲区
ImgBufLength: LongWord;
//图像卡采集的每帧数据数据大小
BufLength: LongWord; pBMIInfo: PBITMAPINFO;
pBuffer: PChar;
Bmp: TBitmap;const
INPUT_WINDOW_X = 0;
INPUT_WINDOW_Y = 0;
INPUT_WINDOW_WIDTH = 400;
INPUT_WINDOW_HEIGHT = 300; OUTPUT_WINDOW_X = 0;
OUTPUT_WINDOW_Y = 0;
OUTPUT_WINDOW_WIDTH = 400;
OUTPUT_WINDOW_HEIGHT = 300;
procedure TForm1.FormCreate(Sender: TObject);
begin
//颜色空间类型,RGB565
ColorSpace := RGB565; //各种图像卡参数设置,用户可更改
VideoStandard := PAL; //采集模式帧方式
DispMode := FRAME; //源路为复合视频一
Source.nType := COMPOSITE_VIDEO;
Source.nIndex := 0; //晶振为35M
CryOsc := CRY_OSC_35M; //亮度128
byBrightness := 128; //对比度128
byContrast := 128; //色调128
byHue := 128; //饱和度128
bySaturation := 128;
if (BeginCGCard(1, hCGCard) = CG_OK) then
begin
bInit := True; //颜色空间类型,按照当前Windows显示属性设置
CG_VERIFY(CGSetVideoFormat(hCGCard, ColorSpace));
//采集模式,帧方式
CG_VERIFY(CGSetScanMode(hCGCard, DispMode));
//源路,默认是第一路
CG_VERIFY(CGSetVideoSource(hCGCard, Source));
//晶振, 默认为35M
CG_VERIFY(CGSelectCryOsc(hCGCard, CryOsc));
//亮度,默认是128
CG_VERIFY(CGAdjustVideo(hCGCard, BRIGHTNESS, byBrightness));
//对比度,默认是128
CG_VERIFY(CGAdjustVideo(hCGCard, CONTRAST, byContrast));
//色调,默认是128
CG_VERIFY(CGAdjustVideo(hCGCard, HUE, byHue));
//包含度,默认是128
CG_VERIFY(CGAdjustVideo(hCGCard, SATURATION, bySaturation));
//设置输入视频窗口大小
CG_VERIFY(CGSetInputWindow(hCGCard, INPUT_WINDOW_X,
INPUT_WINDOW_Y,
INPUT_WINDOW_WIDTH,
INPUT_WINDOW_HEIGHT));
//设置输出图像大小
CG_VERIFY(CGSetOutputWindow(hCGCard, OUTPUT_WINDOW_X,
OUTPUT_WINDOW_Y,
OUTPUT_WINDOW_WIDTH,
OUTPUT_WINDOW_HEIGHT));
end
else
begin
MessageBox(0, 'CGCard Init Failed!', 'Warning', MB_OK or MB_ICONWARNING);
end;
end;
function SnapExCallback(var info: SNAP_INFO): Integer; stdcall
begin Form1.Draw(info.nNumber);
result := 1;
end;
function TForm1.Draw (nNumber: Integer): boolean;
var
MemHandle: LongWord;
LineAddr: PChar;
//Bmp: TBitmap;
VideoDc: HDC;
p: PByteArray;
Gray, x, y: Integer;
begin
VideoDc := GetDc(VideoPanel.Handle);
//已经固定用GRB565来采集数据了,每帧图像768*576,所以图像卡需要的帧缓冲区大小是
BufLength := OUTPUT_WINDOW_WIDTH * OUTPUT_WINDOW_HEIGHT * 2; //用户要不断将采集到静态内存的帧图像数据读出,需要读出后的帧图像数据是24位彩色
//所以用户自行创建的缓冲区大小是
ImgBufLength := OUTPUT_WINDOW_WIDTH * OUTPUT_WINDOW_HEIGHT * 3;
pBuffer := AllocMem(ImgBufLength);
pBMIInfo := AllocMem(sizeof(TBITMAPINFO)+ (255 * sizeof(TRGBQuad)));
Bmp := TBitmap.Create;
//锁定全部保留静态内存给图像卡使用
CG_VERIFY(CGStaticMemLock(Longword(nNumber) * BufLength, BufLength, MemHandle, LineAddr));
SetStretchBltMode(VideoDC, COLORONCOLOR);
Bmp.Width:= OUTPUT_WINDOW_WIDTH;
Bmp.Height := OUTPUT_WINDOW_HEIGHT;
Bmp.PixelFormat := pf24bit;
//设定BMP的头信息
pBMIInfo^.bmiHeader.biSize := sizeof(BITMAPINFOHEADER);
pBMIInfo^.bmiHeader.biWidth := OUTPUT_WINDOW_WIDTH;
pBMIInfo^.bmiHeader.biHeight := OUTPUT_WINDOW_HEIGHT;
pBMIInfo^.bmiHeader.biPlanes := 1;
pBMIInfo^.bmiHeader.biBitCount := 24;
pBMIInfo^.bmiHeader.biCompression := BI_RGB;
pBMIInfo^.bmiHeader.biSizeImage := 0;
pBMIInfo^.bmiHeader.biXPelsPerMeter := 0;
pBMIInfo^.bmiHeader.biYPelsPerMeter := 0;
pBMIInfo^.bmiHeader.biClrUsed := 0;
pBMIInfo^.bmiHeader.biClrImportant := 0;
//读出数据
CGDataTransform(pBuffer , LineAddr , OUTPUT_WINDOW_WIDTH, OUTPUT_WINDOW_HEIGHT, 16, True);
SetStretchBltMode(Bmp.Canvas.Handle, COLORONCOLOR);
SetDIBitsToDevice(Bmp.Canvas.Handle, 0, 0, Bmp.Width, Bmp.Height,
0, 0, 0, pBMIInfo^.bmiHeader.biHeight,
pBuffer, pBMIInfo^, DIB_RGB_COLORS);
randomize;
for y := 0 to Bmp.Height - 1 do
begin
p := Bmp.scanline[y];
for x := 0 to Bmp.Width - 1 do
begin
//一个象素点三个字节
Gray := Round(p[x * 3 + 2] * 0.3 + p[x * 3 + 1] * 0.59 + p[x
* 3] * 0.11);
if gray > 128 then //全局阀值128
begin
p[x * 3] := 255;
p[x * 3 + 1] := 255;
p[x * 3 + 2] := 255;
end
else
begin
p[x * 3] := 0;
p[x * 3 + 1] := 0;
p[x * 3 + 2] := 0;
end;
end;
end;
//StretchDIBits(VideoDC, 0, 0, VideoPanel.Width, VideoPanel.Height,
// 0, 0, pBMIInfo^.bmiHeader.biWidth, pBMIInfo^.bmiHeader.biHeight,
// pBuffer, pBMIInfo^, DIB_RGB_COLORS, SRCCOPY);
Image1.Picture.Bitmap.Assign(Bmp); //用这种方法的显示画面闪烁,而用StretchDIBits不会闪
Bmp.Free;
CG_VERIFY(CGStaticMemUnlock(MemHandle));
ReleaseDC(VideoPanel.Handle, VideoDC);
result := true;
end;
procedure TForm1.StartSnapButtonClick(Sender: TObject);
var
status: CGSTATUS;
begin
if not bInit then Exit;
status := CGOpenSnapEx(hCGCard, @SnapExCallback, nil);
if (CG_SUCCESS(status)) then
begin
//开始启动Snap连续采集到内存,使用缓存区数是2,即双缓冲采集
status := CGStartSnapEx(hCGCard, 0, True, 2);
if ( not CG_SUCCESS(status)) then
CGCloseSnapEx(hCGCard)
else
begin
//更改按钮状态
StopSnapButton.Enabled := True;
StartSnapButton.Enabled := False;
//切换bSanp状态
bSnap := True;
end;
end;
end;procedure TForm1.StopSnapButtonClick(Sender: TObject);
var
status: CGSTATUS;
begin
status := CGCloseSnapEx(hCGCard);
if (CG_SUCCESS(status)) then
begin
bSnap := False;
//更改按钮状态
StopSnapButton.Enabled := False;
StartSnapButton.Enabled := True;
end;
end;procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
if bSnap then
begin
Action := caNone;
MessageBox(0, 'Can''t Exit! Please Stop Snap Operation!', 'Warning', MB_OK or MB_ICONWARNING);
end
else
begin
Action := caFree;
end;
end;procedure TForm1.FormDestroy(Sender: TObject);
begin
//没有打开卡就没有必要关闭了,直接退出
if not bInit then
exit; if (pBMIInfo <> nil) then
FreeMem(pBMIInfo, sizeof(TBITMAPINFO)+ (255 * sizeof(TRGBQuad)));
if (pBuffer <> nil) then
FreeMem(pBuffer, BufLength); //关闭采集卡
CG_VERIFY(EndCGCard(hCGCard));
end;
end.
//用户创建的一帧图像数据缓冲区大小,用来将图像卡采集到内存的数据读出到该缓冲区
ImgBufLength: LongWord;
//图像卡采集的每帧数据数据大小
BufLength: LongWord; pBMIInfo: PBITMAPINFO;
pBuffer: PChar;
Bmp: TBitmap;const
INPUT_WINDOW_X = 0;
INPUT_WINDOW_Y = 0;
INPUT_WINDOW_WIDTH = 400;
INPUT_WINDOW_HEIGHT = 300; OUTPUT_WINDOW_X = 0;
OUTPUT_WINDOW_Y = 0;
OUTPUT_WINDOW_WIDTH = 400;
OUTPUT_WINDOW_HEIGHT = 300;
procedure TForm1.FormCreate(Sender: TObject);
begin
//颜色空间类型,RGB565
ColorSpace := RGB565; //各种图像卡参数设置,用户可更改
VideoStandard := PAL; //采集模式帧方式
DispMode := FRAME; //源路为复合视频一
Source.nType := COMPOSITE_VIDEO;
Source.nIndex := 0; //晶振为35M
CryOsc := CRY_OSC_35M; //亮度128
byBrightness := 128; //对比度128
byContrast := 128; //色调128
byHue := 128; //饱和度128
bySaturation := 128;
if (BeginCGCard(1, hCGCard) = CG_OK) then
begin
bInit := True; //颜色空间类型,按照当前Windows显示属性设置
CG_VERIFY(CGSetVideoFormat(hCGCard, ColorSpace));
//采集模式,帧方式
CG_VERIFY(CGSetScanMode(hCGCard, DispMode));
//源路,默认是第一路
CG_VERIFY(CGSetVideoSource(hCGCard, Source));
//晶振, 默认为35M
CG_VERIFY(CGSelectCryOsc(hCGCard, CryOsc));
//亮度,默认是128
CG_VERIFY(CGAdjustVideo(hCGCard, BRIGHTNESS, byBrightness));
//对比度,默认是128
CG_VERIFY(CGAdjustVideo(hCGCard, CONTRAST, byContrast));
//色调,默认是128
CG_VERIFY(CGAdjustVideo(hCGCard, HUE, byHue));
//包含度,默认是128
CG_VERIFY(CGAdjustVideo(hCGCard, SATURATION, bySaturation));
//设置输入视频窗口大小
CG_VERIFY(CGSetInputWindow(hCGCard, INPUT_WINDOW_X,
INPUT_WINDOW_Y,
INPUT_WINDOW_WIDTH,
INPUT_WINDOW_HEIGHT));
//设置输出图像大小
CG_VERIFY(CGSetOutputWindow(hCGCard, OUTPUT_WINDOW_X,
OUTPUT_WINDOW_Y,
OUTPUT_WINDOW_WIDTH,
OUTPUT_WINDOW_HEIGHT));
end
else
begin
MessageBox(0, 'CGCard Init Failed!', 'Warning', MB_OK or MB_ICONWARNING);
end;
end;
function SnapExCallback(var info: SNAP_INFO): Integer; stdcall
begin Form1.Draw(info.nNumber);
result := 1;
end;
function TForm1.Draw (nNumber: Integer): boolean;
var
MemHandle: LongWord;
LineAddr: PChar;
//Bmp: TBitmap;
VideoDc: HDC;
p: PByteArray;
Gray, x, y: Integer;
begin
VideoDc := GetDc(VideoPanel.Handle);
//已经固定用GRB565来采集数据了,每帧图像768*576,所以图像卡需要的帧缓冲区大小是
BufLength := OUTPUT_WINDOW_WIDTH * OUTPUT_WINDOW_HEIGHT * 2; //用户要不断将采集到静态内存的帧图像数据读出,需要读出后的帧图像数据是24位彩色
//所以用户自行创建的缓冲区大小是
ImgBufLength := OUTPUT_WINDOW_WIDTH * OUTPUT_WINDOW_HEIGHT * 3;
pBuffer := AllocMem(ImgBufLength);
pBMIInfo := AllocMem(sizeof(TBITMAPINFO)+ (255 * sizeof(TRGBQuad)));
Bmp := TBitmap.Create;
//锁定全部保留静态内存给图像卡使用
CG_VERIFY(CGStaticMemLock(Longword(nNumber) * BufLength, BufLength, MemHandle, LineAddr));
SetStretchBltMode(VideoDC, COLORONCOLOR);
Bmp.Width:= OUTPUT_WINDOW_WIDTH;
Bmp.Height := OUTPUT_WINDOW_HEIGHT;
Bmp.PixelFormat := pf24bit;
//设定BMP的头信息
pBMIInfo^.bmiHeader.biSize := sizeof(BITMAPINFOHEADER);
pBMIInfo^.bmiHeader.biWidth := OUTPUT_WINDOW_WIDTH;
pBMIInfo^.bmiHeader.biHeight := OUTPUT_WINDOW_HEIGHT;
pBMIInfo^.bmiHeader.biPlanes := 1;
pBMIInfo^.bmiHeader.biBitCount := 24;
pBMIInfo^.bmiHeader.biCompression := BI_RGB;
pBMIInfo^.bmiHeader.biSizeImage := 0;
pBMIInfo^.bmiHeader.biXPelsPerMeter := 0;
pBMIInfo^.bmiHeader.biYPelsPerMeter := 0;
pBMIInfo^.bmiHeader.biClrUsed := 0;
pBMIInfo^.bmiHeader.biClrImportant := 0;
//读出数据
CGDataTransform(pBuffer , LineAddr , OUTPUT_WINDOW_WIDTH, OUTPUT_WINDOW_HEIGHT, 16, True);
SetStretchBltMode(Bmp.Canvas.Handle, COLORONCOLOR);
SetDIBitsToDevice(Bmp.Canvas.Handle, 0, 0, Bmp.Width, Bmp.Height,
0, 0, 0, pBMIInfo^.bmiHeader.biHeight,
pBuffer, pBMIInfo^, DIB_RGB_COLORS);
randomize;
for y := 0 to Bmp.Height - 1 do
begin
p := Bmp.scanline[y];
for x := 0 to Bmp.Width - 1 do
begin
//一个象素点三个字节
Gray := Round(p[x * 3 + 2] * 0.3 + p[x * 3 + 1] * 0.59 + p[x
* 3] * 0.11);
if gray > 128 then //全局阀值128
begin
p[x * 3] := 255;
p[x * 3 + 1] := 255;
p[x * 3 + 2] := 255;
end
else
begin
p[x * 3] := 0;
p[x * 3 + 1] := 0;
p[x * 3 + 2] := 0;
end;
end;
end;
//StretchDIBits(VideoDC, 0, 0, VideoPanel.Width, VideoPanel.Height,
// 0, 0, pBMIInfo^.bmiHeader.biWidth, pBMIInfo^.bmiHeader.biHeight,
// pBuffer, pBMIInfo^, DIB_RGB_COLORS, SRCCOPY);
Image1.Picture.Bitmap.Assign(Bmp); //用这种方法的显示画面闪烁,而用StretchDIBits不会闪
Bmp.Free;
CG_VERIFY(CGStaticMemUnlock(MemHandle));
ReleaseDC(VideoPanel.Handle, VideoDC);
result := true;
end;
procedure TForm1.StartSnapButtonClick(Sender: TObject);
var
status: CGSTATUS;
begin
if not bInit then Exit;
status := CGOpenSnapEx(hCGCard, @SnapExCallback, nil);
if (CG_SUCCESS(status)) then
begin
//开始启动Snap连续采集到内存,使用缓存区数是2,即双缓冲采集
status := CGStartSnapEx(hCGCard, 0, True, 2);
if ( not CG_SUCCESS(status)) then
CGCloseSnapEx(hCGCard)
else
begin
//更改按钮状态
StopSnapButton.Enabled := True;
StartSnapButton.Enabled := False;
//切换bSanp状态
bSnap := True;
end;
end;
end;procedure TForm1.StopSnapButtonClick(Sender: TObject);
var
status: CGSTATUS;
begin
status := CGCloseSnapEx(hCGCard);
if (CG_SUCCESS(status)) then
begin
bSnap := False;
//更改按钮状态
StopSnapButton.Enabled := False;
StartSnapButton.Enabled := True;
end;
end;procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
if bSnap then
begin
Action := caNone;
MessageBox(0, 'Can''t Exit! Please Stop Snap Operation!', 'Warning', MB_OK or MB_ICONWARNING);
end
else
begin
Action := caFree;
end;
end;procedure TForm1.FormDestroy(Sender: TObject);
begin
//没有打开卡就没有必要关闭了,直接退出
if not bInit then
exit; if (pBMIInfo <> nil) then
FreeMem(pBMIInfo, sizeof(TBITMAPINFO)+ (255 * sizeof(TRGBQuad)));
if (pBuffer <> nil) then
FreeMem(pBuffer, BufLength); //关闭采集卡
CG_VERIFY(EndCGCard(hCGCard));
end;
end.
var
MemHandle: LongWord;
LineAddr: PChar;
//Bmp: TBitmap;
VideoDc: HDC;
p: PByteArray;
Gray, x, y: Integer;
begin
VideoDc := GetDc(VideoPanel.Handle);
//已经固定用GRB565来采集数据了,每帧图像768*576,所以图像卡需要的帧缓冲区大小是
BufLength := OUTPUT_WINDOW_WIDTH * OUTPUT_WINDOW_HEIGHT * 2; //用户要不断将采集到静态内存的帧图像数据读出,需要读出后的帧图像数据是24位彩色
//所以用户自行创建的缓冲区大小是
ImgBufLength := OUTPUT_WINDOW_WIDTH * OUTPUT_WINDOW_HEIGHT * 3;
pBuffer := AllocMem(ImgBufLength);
pBMIInfo := AllocMem(sizeof(TBITMAPINFO)+ (255 * sizeof(TRGBQuad)));
Bmp := TBitmap.Create;
//锁定全部保留静态内存给图像卡使用
CG_VERIFY(CGStaticMemLock(Longword(nNumber) * BufLength, BufLength, MemHandle,
LineAddr));
SetStretchBltMode(VideoDC, COLORONCOLOR);
Bmp.Width:= OUTPUT_WINDOW_WIDTH;
Bmp.Height := OUTPUT_WINDOW_HEIGHT;
Bmp.PixelFormat := pf24bit;
//设定BMP的头信息
pBMIInfo^.bmiHeader.biSize := sizeof(BITMAPINFOHEADER);
pBMIInfo^.bmiHeader.biWidth := OUTPUT_WINDOW_WIDTH;
pBMIInfo^.bmiHeader.biHeight := OUTPUT_WINDOW_HEIGHT;
pBMIInfo^.bmiHeader.biPlanes := 1;
pBMIInfo^.bmiHeader.biBitCount := 24;
pBMIInfo^.bmiHeader.biCompression := BI_RGB;
pBMIInfo^.bmiHeader.biSizeImage := 0;
pBMIInfo^.bmiHeader.biXPelsPerMeter := 0;
pBMIInfo^.bmiHeader.biYPelsPerMeter := 0;
pBMIInfo^.bmiHeader.biClrUsed := 0;
pBMIInfo^.bmiHeader.biClrImportant := 0;
//读出数据
CGDataTransform(pBuffer , LineAddr , OUTPUT_WINDOW_WIDTH, OUTPUT_WINDOW_HEIGHT, 16, True);
SetStretchBltMode(Bmp.Canvas.Handle, COLORONCOLOR);
SetDIBitsToDevice(Bmp.Canvas.Handle, 0, 0, Bmp.Width, Bmp.Height,
0, 0, 0, pBMIInfo^.bmiHeader.biHeight,
pBuffer, pBMIInfo^, DIB_RGB_COLORS);
randomize;
for y := 0 to Bmp.Height - 1 do
begin
p := Bmp.scanline[y];
for x := 0 to Bmp.Width - 1 do
begin
//一个象素点三个字节
Gray := Round(p[x * 3 + 2] * 0.3 + p[x * 3 + 1] * 0.59 + p[x
* 3] * 0.11);
if gray > 128 then //全局阀值128
begin
p[x * 3] := 255;
p[x * 3 + 1] := 255;
p[x * 3 + 2] := 255;
end
else
begin
p[x * 3] := 0;
p[x * 3 + 1] := 0;
p[x * 3 + 2] := 0;
end;
end;
end;
Image1.Picture.Bitmap.Assign(Bmp); //用这种方法的显示画面闪烁,而用StretchDIBits不会闪
Bmp.Free;
CG_VERIFY(CGStaticMemUnlock(MemHandle));
ReleaseDC(VideoPanel.Handle, VideoDC);
result := true;
end;
Image1.Picture.Bitmap.freeimage;
2.加上try...except脱离delphi环境找一下出错的大范围。
我加上Image1.Picture.Bitmap.freeimage;这句后,运行4到5分钟后,还是弹出那个出错对话框,另外还弹出个中文对话框,说虚拟内存太低
我按你们说的方法试了下,在运行后,该程序所占用内存不断攀升,在一分钟左右时达到峰值153200K左右,接着就下降并稳定在20000K左右,GDI对象一直在56到61之间。运行到四分钟左右时,程序死了。我电脑配置是256M内存,程序运行时系统可用内存一直在2500到4100之间变动。
请问我这个程序是内存泄漏的问题还是另有元凶?谢谢大家了!
我怎么就没有看到释放呢?
pBuffer := AllocMem(ImgBufLength);
pBMIInfo := AllocMem(sizeof(TBITMAPINFO)+ (255 * sizeof(TRGBQuad)));
那两个东东的释放在顶楼贴子最后面的那个procedure中,即:procedure TForm1.FormDestroy(Sender: TObject);
begin
//没有打开卡就没有必要关闭了,直接退出
if not bInit then
exit; if (pBMIInfo <> nil) then
FreeMem(pBMIInfo, sizeof(TBITMAPINFO)+ (255 * sizeof(TRGBQuad)));
if (pBuffer <> nil) then
FreeMem(pBuffer, BufLength); //关闭采集卡
CG_VERIFY(EndCGCard(hCGCard));
end;