Hi (
hoffe das Ganze hier ist nicht zu lang).
>> Wie wandle ich das Rohmaterial eines gegrabten Frames (von einer Webcam) wieder in ein korrektes Bitmap um?
Es geht also wieder mal um das Auslesen einer Webcam, bzw. wie man davon z.B. jede Sekunde ein Bild (Frame) "abzweigt" (etwa um es danach untersuchen zu können) ...und was insgesamt auch dann funktionieren sollte, wenn das Prog. im Hintergrund läuft und man das Clipboard für andere Applications freizuhalten hat.
Code-mäßig aufgebaut habe ich meinen Versuch dabei auf
diesen Thread hier bzw. das was Ultimator dort anfangs postete. Im Unterschied zu diesem Verfahren benutze ich jetzt aber nicht das Clipboard, sondern eine (Frame-) Callback-Funktion, welche mittels dieser WM installiert wird:
WM_CAP_SET_CALLBACK_FRAME = WM_User + 5;
..plus nochmal dem Creator mit der dazugehörenden SendMessage-Zeile:
Delphi-Quellcode:
procedure TForm1.FormCreate(Sender: TObject);
begin
GrabFrameFlag := false;
VHandle := capCreateCaptureWindow('Video',ws_child+ws_visible, 0, 0,
640, 480, Panel1.Handle, 1);
SendMessage(VHandle, WM_CAP_DRIVER_CONNECT, 0, 0);
SendMessage(VHandle, WM_CAP_SET_PREVIEWRATE, 15, 0);
sendMessage(VHandle, WM_CAP_SET_OVERLAY, 1, 0);
SendMessage(VHandle, wm_cap_set_preview, 1, 0);
SendMessage(VHandle, WM_CAP_SET_CALLBACK_FRAME, 0, integer(@FrameCallbackFunction));
SendMessage(VHandle,WM_CAP_DLG_VIDEOFORMAT,1,0);
VDC := getDC(VHandle);
hCompatibleBitmap := CreateCompatibleBitmap(VDC,640,480);
end;
Wie man am Ende des Creators sieht, versuche ich dort noch ein zum CaptureWindow kompatibles Bitmap zu erstellen, in welches ich in der Callback-Funktion dann das Rohmaterial des Frames hineinzuladen versuche.
...und schließlich also die Callback-Funktion selbst (plus einem Recordtyp, nötig für den zweiten Parameter der Parameterliste: "lpVHdr: pointer to struct containing captured frame information")
Delphi-Quellcode:
Type
TVIDEOHDR = record
lpData : Pointer; // address of video buffer
dwBufferLength : DWord; // size, in bytes, of the Data buffer
dwBytesUsed : DWord; // see below
dwTimeCaptured : DWord; // see below
dwUser : DWord; // user-specific data
dwFlags : DWord; // see below
dwReserved1, dwReserved2, dwReserved3 : DWord; // reserved; do not use
end;
TVideoHDRPtr = ^TVideoHDR;
function FrameCallbackFunction(AHandle : hWnd; VIDEOHDR : TVideoHDRPtr): bool; stdcall;
var ACount : integer;
begin
result := true;
// da diese Callback-Funktion sonst bei jedem Preview-Frame ganz durchlaufen werden würde:
// jetzt nur noch mehr dann, wenn draußen GrabFrameFlag auf TRUE gesetzt wurde PLUS, für die
// Hintergrundlaufbarkeit: SendMessage(VHandle, WM_CAP_GRAB_FRAME_NOSTOP, 1, 0);
if GrabFrameFlag = false then exit;
GrabFrameFlag := false;
windows.beep(1000,10); // nur nochmal zum akustischen Besuchs-Check
ACount := SetBitmapBits(hCompatibleBitmap,VideoHDR^.dwBufferLength,VideoHDR^.lpData);
with form2 do begin
FBitmap.Handle := hCompatibleBitmap;
PaintBox1.Repaint; // hier wird dann der Inhalt von FBitmap auf form2.PaintBox1 ausgegeben.
end;
end;
Wie man am Code in der Callback-Funktion sieht, versuche ich dort jetzt mittels der ApiFunktion 'SetBitmapBits' das Rohmaterial des Frames in ein zum CaptureWindow compatibles Bitmap zu laden. Anschließend übertrage ich es dann
Handle-mäßig auf ein
VCL-Bitmap (FBitmap), welches schließlich in einer Paintbox ausgegeben wird. NUR – kann man das alles überhaupt so machen? (Oder/aber, warum eigentlich auch wieder nicht, bzw. wie sonst?)
Jedenfalls besteht das dabei herauskommende Bild dann aber leider nur aus irgendwelchen Farbschlieren und ist im unteren Drittel völlig schwarz. Besonders auch letzteres legt dann irgendwie die Vermutung nahe, dass das Frame-Rohmaterial vor oder während des Transfers wahrscheinlich wenigstens noch hätte dekomprimiert werden müssen.
In einem aufrufbaren Dialog-Window (bzgl. des einstellbaren (Frame-) Formates) wird ja dann auch zufällig noch mit angezeigt, dass die Komprimierung vom Typ I420 ist und ein Bild 460800 Bytes groß. Das hieße also bei 640*480 Pixels pro Pixel 12 Bits?!?
Frage wäre hier also vor allem auch: Wie kann man für das so vorliegendes Rohmaterial eine Dekomprimierung durchführen??? Oder wo könnten sonst Fehler in der Art sein, wie ich das oben insgesamt versuche? Bzw. gibt es sonst noch irgendwelche einfacheren Alternativen dazu?
Irgendwelche Ideen?