Autor |
Beitrag |
Heiko
      
Beiträge: 3169
Erhaltene Danke: 11
|
Verfasst: Fr 22.06.07 19:35
Hallo, ich habe demnächst wieder Praktikum und arbeite dann an der WebCam-Bildanalyse weiter. Dabei ist mir mal wieder aufgefallen, dass ich haufen FPS verliere beim abgreifen der Bilder. Sprich ich bekomme vom TWAIN-Treiber die Adresse zurückgeliefert, wo die Bilder liegen, und kopiere mir die dann in ein BMP um es ausgeben zu können. Aber das kopieren dauert scheinbar zu lange. Gibt es da eine effizientere Variante? Bisheriger Code: 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65:
| procedure GetData(hCapWnd: HWND; lpVHDR: PVideoHdr; var BMP: TBitmap); var BitmapInfo: TBitmapInfo; src: PRGBA; x, y: integer; Width, Height: Integer; begin FillChar(BitmapInfo, SizeOf(BitmapInfo), 0); SendMessage(hCapWnd, WM_CAP_GET_VIDEOFORMAT, SizeOf(BitmapInfo), Integer(@BitmapInfo)); Width:=BitmapInfo.bmiHeader.biWidth; Height:=BitmapInfo.bmiHeader.biHeight;
if (BMP.Width<>Width) or (BMP.Height<>Height) then begin BMP.SetSize(Width, Height); end;
src:=PRGBA(lpVHdr^.lpData); if BitmapInfo.bmiHeader.biCompression = BI_RGB then begin case BitmapInfo.bmiHeader.biBitCount of 24: begin for y := Height-1 downto 0 do begin for x := Width-1 downto 0 do begin BMP.Canvas.Pixels[x, y]:=(Src^.B shl 16) or (Src^.G shl 8) or Src^.R; Src:=PRGBA(Integer(Src)+3); end; end; end; 32: begin for y := Height-1 downto 0 do begin for x := Width-1 downto 0 do begin BMP.Canvas.Pixels[x, y]:=(Src^.B shl 16) or (Src^.G shl 8) or Src^.R; Src:=PRGBA(Integer(Src)+4); end; end; end; end; end; end;
function OnFrame(hCapWnd: HWND; lpVHDR: PVideoHdr): DWord; stdcall; var newtime: Cardinal; begin Result:=1; GetData(hCapWnd, lpVHDR, BMP); BitBlt(Form1.Canvas.Handle, 0, 0, BMP.Width, BMP.Height, BMP.Canvas.Handle, 0, 0, SRCCOPY); newtime:=GetTickCount; Form1.Caption:='CamAnalyzer - '+Format('%.2f', [1000/(newtime-Lasttime)])+'FPS'; LastTime:=newtime; end;
procedure TForm1.FormCreate(Sender: TObject); begin BMP:=TBitmap.Create; CapHandle:=capCreateCaptureWindow('CamStream', WS_CHILD or WS_VISIBLE, 0, 0, 1, 1, Handle, 1); SendMessage(CapHandle, WM_CAP_DRIVER_CONNECT, 0, 0); SendMessage(CapHandle, WM_CAP_SET_CALLBACK_FRAME, 0, Integer(@OnFrame)); SendMessage(CapHandle, WM_CAP_SET_PREVIEWRATE, 1, 0); end; | Und würde es vlt. etwas bringen das abfassen in einen extra Thread zu lagern (GUI entlasten)? Da wüsste ich jetzte nur nicht, wie ich den dazubringe, dass OnFrame in den anderen Frame gehört. Hat da einer vlt. auch eine Idee? So könnte ich den CPU stärker auslasten beim auswerten etc. Grüße Heiko //EDIT: Am liebste wäre mir eine Variante ohne BMP, also nur mit einem "Array"  -
|
|
Phantom1
      
Beiträge: 390
|
Verfasst: Fr 22.06.07 19:54
Du arbeitest mit BMP.Canvas.Pixels[x,y} was sehr langsam ist, wesentlich schneller gehts mit scanline.
Desweiteren kopierst du das Bitmap mit BitBlt auf die Form, das kostet auch ganz schön zeit. Mit etwas aufwand kann man das auch durch DirectX funktionen etc machen.
|
|
Heiko 
      
Beiträge: 3169
Erhaltene Danke: 11
|
Verfasst: Fr 22.06.07 20:17
Phantom1 hat folgendes geschrieben: | Du arbeitest mit BMP.Canvas.Pixels[x,y} was sehr langsam ist, wesentlich schneller gehts mit scanline. |
Mhm stimmt. Wieder einmal außer acht gelassen  . Aber gehts auch ohne Scanline? Denn ich gehe ja hintereinander durch, da würde es mir reichen von 0 bis zuir Größe zu gehen. Aber da habe ich bisher nichts funktionierendes hinbekommen... (muss noch einmal probieren, obs ab scanline[0], da ich nimmer weiß, ob ich das getestet hatte )
Phantom1 hat folgendes geschrieben: | Desweiteren kopierst du das Bitmap mit BitBlt auf die Form, das kostet auch ganz schön zeit. Mit etwas aufwand kann man das auch durch DirectX funktionen etc machen. |
Das verbraucht laut Messungen 0ms, von daher dürfte das eher uninteressant sein  .
|
|
Corpsman
      
Beiträge: 228
KUbuntu 10.4
Lazarus
|
Verfasst: Fr 22.06.07 20:28
hossa,
Ja es geht auch ohne Scanline, aber dazu müstest du dich mit Pointern auskennen.
und das Pixelformat der Bitmaps vorher festlegen.
Wenn du es mit OpenGL machen würdest wäre es aber noch einfacher, da kannst du den Pointer einfach weiterreichen und den Rest macht dan die Grake
_________________ --
Just Try it.
|
|
Heiko 
      
Beiträge: 3169
Erhaltene Danke: 11
|
Verfasst: Fr 22.06.07 20:35
Corpsman hat folgendes geschrieben: | Ja es geht auch ohne Scanline, aber dazu müstest du dich mit Pointern auskennen. |
Pointer sind kein Problem  . Wenn ichs nicht könnte, würde ich auch keine 1000Zeiler hinbekommen, die machen was sie sollen.
Corpsman hat folgendes geschrieben: | Wenn du es mit OpenGL machen würdest wäre es aber noch einfacher, da kannst du den Pointer einfach weiterreichen und den Rest macht dan die Grake |
OpenGL-Grundsachen sind weitestgehend kein Problem, aber sobald es um soetwas geht schon. Kannste mal kurz zeigen, wie das mit OpenGL geht?
|
|
BenBE
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: So 24.06.07 22:15
Wenn Du die Case in Zeile 21 weg lässt kannst Du den Block, den Du für 24 Bit nutzt auch für 32-Bit benutzen, wenn Du die die Größe eines Pixels (3\4 Byte) einmal ausrechnest und dann in der Pointer-Addition verwendest.
Außerdem kannst Du Dir das Drehen der Farbkanäle schenken, wenn Du gleich ein Bitmap-Handle anforderst, bei dem Du BGR im Speicher ablegen musst.
_________________ Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
|
|
Heiko 
      
Beiträge: 3169
Erhaltene Danke: 11
|
Verfasst: Mo 25.06.07 07:42
BenBE hat folgendes geschrieben: | Außerdem kannst Du Dir das Drehen der Farbkanäle schenken, wenn Du gleich ein Bitmap-Handle anforderst, bei dem Du BGR im Speicher ablegen musst. |
Du meinst das umdrehen durch die downto-Schleife? Eigentlich war das eher unbeabsichtitg  . Ein Vergleich mit 0 ist nun einmal schneller, von daher rückwärts. Das umdrehen war nur ein Randeffekt  .
Ach der neue Code, der ca. 16x schneller ist als der alte (+CPU-Last von 50% auf 2% gesenkt):
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128:
| procedure GetData(hCapWnd: HWND; lpVHDR: PVideoHdr; var BMP: TBitmap; bmiHeader: TBitmapInfoHeader); var src: PRGBA; i: integer; p: PRGB; pz: Integer; begin src:=PRGBA(lpVHdr^.lpData); if bmiHeader.biCompression = BI_RGB then begin pz:=bmiHeader.biBitCount div 8; p:=BMP.ScanLine[BMP.Height-1]; for i:= (bmiHeader.biSizeImage div 3)-1 downto 0 do begin p^.B:=Src^.B; p^.G:=Src^.G; p^.R:=Src^.R; Src:=PRGBA(Integer(Src)+pz); inc(p); end; end; end;
procedure InterpretFrame; var i: integer; p: array[0..1] of PRGB; r: PRGB; begin p[0]:=BMP[0].ScanLine[BMP[0].Height-1]; p[1]:=BMP[1].ScanLine[BMP[0].Height-1]; r :=ResBMP.ScanLine[BMP[0].Height-1]; for i := ResBMP.Height*ResBMP.Width-1 downto 0 do begin if (abs(p[0]^.B - p[1]^.B) < 20) and (abs(p[0]^.R - p[1]^.R) < 20) and (abs(p[0]^.G - p[1]^.G) < 20) then begin r^.R:=p[curBMP]^.R; r^.G:=p[curBMP]^.G; r^.B:=p[curBMP]^.B; end else begin r^.R:=0; r^.G:=255; r^.B:=0; end; inc(r); inc(p[0]); inc(p[1]); end; end;
function OnFrame(hCapWnd: HWND; lpVHDR: PVideoHdr): DWord; stdcall; var newtime: Cardinal; begin Result:=1; curBMP:=curBMP xor 1; GetData(hCapWnd, lpVHDR, BMP[curBMP], bmiHeader); InterpretFrame; BitBlt(Form1.Canvas.Handle, 0, 0, ResBMP.Width, ResBMP.Height, ResBMP.Canvas.Handle, 0, 0, SRCCOPY); newtime:=GetTickCount; if newtime<>Lasttime then Form1.Caption:='CamAnalyzer - '+Format('%.2f', [1000/(newtime-Lasttime)])+'FPS'; LastTime:=newtime; end;
procedure ChangeFormat; var BitmapInfo: TBitmapInfo; Width, Height: Integer; begin FillChar(BitmapInfo, SizeOf(BitmapInfo), 0); SendMessage(CapHandle, WM_CAP_GET_VIDEOFORMAT, SizeOf(BitmapInfo), Integer(@BitmapInfo)); Width:=BitmapInfo.bmiHeader.biWidth; Height:=BitmapInfo.bmiHeader.biHeight;
if (curWidth<>Width) or (curHeight<>Height) then begin curWidth:=Width; curHeight:=Height; BMP[0].SetSize(Width, Height); BMP[1].SetSize(Width, Height); ResBMP.SetSize(Width, Height); end; bmiHeader:=BitmapInfo.bmiHeader; end;
procedure TForm1.CamFormatButtonClick(Sender: TObject); begin SendMessage(CapHandle, WM_CAP_DLG_VIDEOFORMAT, 0, 0); ChangeFormat; end;
procedure TForm1.CamSourceButtonClick(Sender: TObject); begin SendMessage(CapHandle, WM_CAP_DLG_VIDEOSOURCE, 0, 0); ChangeFormat; end;
procedure TForm1.FormCreate(Sender: TObject); begin curWidth:=0; curHeight:=0; curBMP:=0; ResBMP:=TBitmap.Create; ResBMP.PixelFormat:=pf24bit; BMP[0]:=TBitmap.Create; BMP[0].PixelFormat:=pf24bit; BMP[1]:=TBitmap.Create; BMP[1].PixelFormat:=pf24bit; ResBMP.PixelFormat:=pf24bit; CapHandle:=capCreateCaptureWindow('CamStream', WS_CHILD or WS_VISIBLE, 0, 0, 1, 1, Handle, 1); SendMessage(CapHandle, WM_CAP_DRIVER_CONNECT, 0, 0); SendMessage(CapHandle, WM_CAP_SET_CALLBACK_FRAME, 0, Integer(@OnFrame)); SendMessage(CapHandle, WM_CAP_SET_PREVIEWRATE, 1, 0); ChangeFormat; LastTime:=GetTickCount; end;
procedure TForm1.FormDestroy(Sender: TObject); begin SendMessage(CapHandle, WM_CAP_DRIVER_DISCONNECT, 0, 0); BMP[0].Free; BMP[1].Free; ResBMP.Free; end; |
|
|
alzaimar
      
Beiträge: 2889
Erhaltene Danke: 13
W2000, XP
D6E, BDS2006A, DevExpress
|
Verfasst: Mo 25.06.07 09:59
TGraphics32 wäre auch noch erwähnenswert.
Dessenungeachtet liegt ein Bitmap als Array of Byte im Speicher. Wie hier schon erwähnt wurde, kannst du eine einfache MOVE-Operation des Frames durchführen (Framepointer der WebCam => Bitmap), sofern das Bild- und Pixelformat bekannt ist.
_________________ Na denn, dann. Bis dann, denn.
|
|
|