Benutzer mit den meisten Antworten
Auf versteckten Videostream einer webcam bildweise zugreifen?

Frage
-
Hallo,
ich benutze eine WM_CAP_DRIVER Class (unten angefügt). Das Video wird gestartet und während es läuft lese ich bildweise Informationen aus.
Das klappt auch sehr gut, aber NUR wenn ich das Video gleichzeitig sehe! Weil ich wenig Platz habe, soll das Video in einer Tabpage laufen während ich in einer anderen Tabpage auf den Stream bildweise zugreifen möchte (Positionierung eines Roboters). Das klappt nicht, erst wieder wenn ich kurz den Tabpage mit dem Video öffne, dann bekomme ich das mir zuletzt angezeigte Videobild in der anderen Tabpage.
Wie kann ich dieses Problem lösen? Bisher starte und beende ich den Stream jedes mal, wenn ich ein Standbild sehen möchte (dafür muss ich den Stream aber nicht sehen), aber als gute Lösung sehe ich das nicht an!
Gruß, Sebastian
Public Class Win32Api #Region "Api Functions" Private Declare Auto Function capCreateCaptureWindow Lib "avicap32.dll" ( _ ByVal lpszWindowName As String, _ ByVal dwStyle As Integer, _ ByVal x As Integer, _ ByVal y As Integer, _ ByVal nWidth As Integer, _ ByVal nHeight As Integer, _ ByVal hWnd As IntPtr, _ ByVal nID As Integer) _ As IntPtr Private Declare Auto Function SendMessage Lib "user32.dll" ( _ ByVal hwnd As IntPtr, _ ByVal uMsg As Integer, _ ByVal wParam As Integer, _ ByVal lParam As Integer) _ As Integer #End Region #Region "Constants" Private Const WM_USER As Int32 = &H400 Private Const WS_CHILD As Integer = &H40000000 Private Const WS_VISIBLE As Integer = &H10000000 Private Const WM_CAP_START As Integer = WM_USER Private Const WM_CAP_DRIVER_CONNECT As Integer = (WM_CAP_START + 10) Private Const WM_CAP_SET_PREVIEWRATE As Integer = (WM_CAP_START + 52) Private Const WM_CAP_SET_OVERLAY As Integer = (WM_CAP_START + 51) Private Const WM_CAP_SET_PREVIEW As Integer = (WM_CAP_START + 50) Private Const WM_CAP_DRIVER_DISCONNECT As Integer = (WM_CAP_START + 11) Private Const WM_CAP_EDIT_COPY As Integer = (WM_CAP_START + 30) #End Region #Region "Private" Private _hwnd As IntPtr Private _width As Integer Private _height As Integer #End Region #Region "Camera Id" Private Const CameraId As Integer = 0 #End Region #Region "Frames" Private Const Frames As Integer = 10 #End Region #Region "Positions" Private x As Integer = 0 Private y As Integer = 0 #End Region #Region "Public" Public MyHandle As IntPtr #End Region #Region "Constructor" Public Sub New(ByVal hWnd As IntPtr, ByVal Width As Integer, ByVal Height As Integer) If Not hWnd.Equals(IntPtr.Zero) Then Me._hwnd = hWnd If (Width >= 320) And (Height >= 240) Then Me._width = Width : Me._height = Height Else Return End If Else Return End If End Sub #End Region #Region "Functions" Public Function GetCaptureHandle() As IntPtr Dim [Handle] As IntPtr = Win32Api.capCreateCaptureWindow("CaptureWindow", _ Win32Api.WS_CHILD + Win32Api.WS_VISIBLE, _ x, y, _ Me._width, Me._height, _ Me._hwnd, _ Win32Api.CameraId) SendMessage([Handle], Win32Api.WM_CAP_DRIVER_CONNECT, Win32Api.CameraId, 0) SendMessage([Handle], Win32Api.WM_CAP_SET_PREVIEWRATE, Win32Api.Frames, 0) SendMessage([Handle], Win32Api.WM_CAP_SET_OVERLAY, 1, 0) SendMessage([Handle], Win32Api.WM_CAP_SET_PREVIEW, 1, 0) If Not [Handle].Equals(IntPtr.Zero) Then Return [Handle] Else Return IntPtr.Zero End If End Function Public ReadOnly Property GetImage() As Drawing.Image Get Return Me.SetCurrentImageToClipBoard() End Get End Property Private Function SetCurrentImageToClipBoard() As Drawing.Image Try My.Computer.Clipboard.Clear() SendMessage(Me.SetHandle(), Win32Api.WM_CAP_EDIT_COPY, 0, 0) Dim img As Image = My.Computer.Clipboard.GetImage If img IsNot Nothing Then Return img Else Return Nothing End If Catch Return Nothing End Try End Function Public Property SetHandle() As IntPtr Get Return MyHandle End Get Set(ByVal value As IntPtr) MyHandle = value End Set End Property Public Sub DisposeConnection() Dim result As Integer = SendMessage(Me.SetHandle(), Win32Api.WM_CAP_DRIVER_DISCONNECT, Win32Api.CameraId, 0) Debug.WriteLine("Disconnected: " & result.ToString()) End Sub #End Region End Class
[VB express 2010]
Antworten
-
Hallo Sebastian,
ich hatte Zeit mich etwas intensiver mit dem Thema zu beschäftigen. Das iCam Beispiel sollte man dabei nach Möglichkeit komplett vergessen oder als Beispiel hernehmen, wie man es nicht machen sollte. Als AnyCPU/64 Bit ausgeführt ist es nicht lauffähig, was abenteuerlichen API Deklarationen und Umwandlungen geschuldet ist. Zudem es das Extrahieren eines Frames nur über die BitBlt/Screencopy Methode realisiert und damit auch nur dann funktioniert, wenn das Capture Window sichtbar ist, wie Du schon richtig vermutet hast.
Zur Lösung Deines Problems: Du kannst einzelne Bilder von der WebCam explizit über die Message WM_CAP_GRAB_FRAME_NOSTOP anfordern. Diese legt eine Momentaufnahme im Framebuffer des Capturewindow ab, auch wenn dieses gerade nicht angezeigt wird. Dieses Image kann dann beispielsweise in die Zwischenablage kopiert werden:
Private Function SetCurrentImageToClipBoard() As Drawing.Image Try My.Computer.Clipboard.Clear() SendMessage(Me.SetHandle(), Win32Api.WM_CAP_GRAB_FRAME_NOSTOP, 0, 0) SendMessage(Me.SetHandle(), Win32Api.WM_CAP_EDIT_COPY, 0, 0) Dim img As Image = My.Computer.Clipboard.GetImage Return img Catch Return Nothing End Try End Function
Deklaration für die Konstante:
Private Const WM_CAP_GRAB_FRAME_NOSTOP As Integer = (WM_CAP_START + 61)
Die Callback Variante verhält sich hier im Übrigen genauso. Das heißt, der Callback wird nur aufgerufen, solange das Capturewindow sichtbar ist oder wenn man explizit ein Frame anfordert. Die Lösung hätte dennoch den Charme, dass man auf den Umweg über die Zwischenablage verzichten kann. Bei Interesse kannst Du ja Bescheid geben.
Thorsten Dörfler
Microsoft MVP Visual Basic
vb-faq.de- Als Antwort markiert Thorsten Dörfler Montag, 11. April 2011 18:42
Alle Antworten
-
Hallo Sebastian,
ungeprüft, könnte die capVideoStreamCallback Function eine Lösung sein:
capVideoStreamCallback
http://msdn.microsoft.com/en-us/library/dd797759.aspx
Problems getting capVideoStreamCallback and WM_CAP_SET_CALLBACK_VIDEOSTREAM to work
http://stackoverflow.com/questions/3876945/problems-getting-capvideostreamcallback-and-wm-cap-set-callback-videostream-to-woFerner bietet folgende Klasse Methoden einzelne Frames aus dem Stream zu fischen:
iCam - A WebCam Class in Visual Basic
http://www.codeguru.com/vb/gen/vb_misc/gamesandfun/article.php/c13951
Thorsten Dörfler
Microsoft MVP Visual Basic
vb-faq.de -
Hallo Thorsten,
danke für die links, die sahen auf den ersten Blick wirklich sehr gut aus! Der erste link bringt mich leider nicht weiter, da ich davon dann doch zu wenig Ahnung und Zeit habe... müsste ja meine Klasse umschreiben.
Das Projekt des zweiten links ist leider unvollständig, das probiere ich lieber nicht. Mit dem dritten link (iCam) hatte ich schon einmal geliebäugelt, aber das "fischen" aus einem stream bezieht sich dabei auf das herauskopieren aus der picturebox in welche der stream gesendet wird. Sieht man die picturebox mit dem stream nicht, ist das bild der picturebox leer. (das ist meine Vermutung, weil es sich mit meinem Beispiel genauso verhält).
Wenn ich am Wochenende Zeit habe, werde ich es trotzdem mal schnell testen!
Gruß, Sebastian
[VB express 2010] -
Hallo Sebastian,
ich hatte Zeit mich etwas intensiver mit dem Thema zu beschäftigen. Das iCam Beispiel sollte man dabei nach Möglichkeit komplett vergessen oder als Beispiel hernehmen, wie man es nicht machen sollte. Als AnyCPU/64 Bit ausgeführt ist es nicht lauffähig, was abenteuerlichen API Deklarationen und Umwandlungen geschuldet ist. Zudem es das Extrahieren eines Frames nur über die BitBlt/Screencopy Methode realisiert und damit auch nur dann funktioniert, wenn das Capture Window sichtbar ist, wie Du schon richtig vermutet hast.
Zur Lösung Deines Problems: Du kannst einzelne Bilder von der WebCam explizit über die Message WM_CAP_GRAB_FRAME_NOSTOP anfordern. Diese legt eine Momentaufnahme im Framebuffer des Capturewindow ab, auch wenn dieses gerade nicht angezeigt wird. Dieses Image kann dann beispielsweise in die Zwischenablage kopiert werden:
Private Function SetCurrentImageToClipBoard() As Drawing.Image Try My.Computer.Clipboard.Clear() SendMessage(Me.SetHandle(), Win32Api.WM_CAP_GRAB_FRAME_NOSTOP, 0, 0) SendMessage(Me.SetHandle(), Win32Api.WM_CAP_EDIT_COPY, 0, 0) Dim img As Image = My.Computer.Clipboard.GetImage Return img Catch Return Nothing End Try End Function
Deklaration für die Konstante:
Private Const WM_CAP_GRAB_FRAME_NOSTOP As Integer = (WM_CAP_START + 61)
Die Callback Variante verhält sich hier im Übrigen genauso. Das heißt, der Callback wird nur aufgerufen, solange das Capturewindow sichtbar ist oder wenn man explizit ein Frame anfordert. Die Lösung hätte dennoch den Charme, dass man auf den Umweg über die Zwischenablage verzichten kann. Bei Interesse kannst Du ja Bescheid geben.
Thorsten Dörfler
Microsoft MVP Visual Basic
vb-faq.de- Als Antwort markiert Thorsten Dörfler Montag, 11. April 2011 18:42
-
Jaaa, voll gut, vielen vielen Dank! Funktioniert einwandfrei :-)
Jetzt ist allerdings ein neues Problem aufgetaucht, denn die Kamera regelt die Helligkeit nicht mehr selbst! Ich habe mal zwei Bilder angehängt, in beiden liegt meine Schwelle im Histogramm bei 100. Das Bild, welches ich durch deine Methode aufgenommen habe, ist deutlich dunkler und mein Objekt wird nicht mehr fehlerfrei erkannt.
Bilder: https://profiles.google.com/115345283700031324241/photos
Von dem An-und Ausschalten der Kamera bin ich übrigens weg und habe das Video immer als Hintergrund meiner Benutzeroberfläche laufen lassen, das hellere Bild von beiden ist damit aufgenommen.
Könnte das doch an der Callback Funktion liegen? Beim Start der USB-Kamera in anderen Programmen braucht sie etwa 1-2 Sekunden um die Helligkeit zu regeln. Das würde etwa 15-30 Bilder entsprechen. Effektiv nehme ich aber bisher nur 5-6 Bilder nach dem Start auf. Könnte es sein, dass ich erstmal 30 Bilder abholen muss, bevor sie "betriebsbereit" ist? Würde sich das über Callback-Funktion lösen lassen?
Gruß, Sebastian
[VB express 2010]- Bearbeitet Thorsten Dörfler Montag, 11. April 2011 16:41 URL als Link
-
Hallo,
wenn ich einen Snapshot direkt nach dem Aktivieren das Cam abhole, ist dieser auch deutlicher dunkler, als ein späterer. Warte ich eine Weile (ca. 10 sek.), während das aktuelle Cam Bild ausgeblendet ist, habe ich aber ein ganz normal ausgeleuchtetes Bild. Ggf. kannst Du hier etwas an den allgemeinen Optionen der Cam drehen. Für Vergleichsaufnahmen wäre vielleicht auch eine konstante Ausleuchtung, sowie fix justierte Helligkeit/Empfindlichkeit von Vorteil. Das hilft jedoch nur, wenn Du diese Faktoren beeinflussen kannst bzw. sie in der Praxis später beeinflussbar sind.
Thorsten Dörfler
Microsoft MVP Visual Basic
vb-faq.de -
Hallo Thorsten,
ja du hast Recht, nach einer Weile sind die Bilder heller. Ich mache es jetzt so, dass ich zuerst 20 Bilder über eine Sekunde abhole, in der Zeit verfährt der Motor eh noch. Merkt also kein Mensch :-)
Danach kann ich mit deiner Methode einzelne Bilder abholen die auch gut ausgeleuchtet sind. Manuell kann man bei der Kamera nichts einstellen....
Vielen Dank für deine Hilfe, der Thread kann damit als gelöst betrachtet werden!
Gruß, Sebastian
[VB express 2010] -
Hallo,
es ist leider ein weiteres Problem aufgetreten. In meinem Fall habe ich zwei USB-Kameras angeschlossen. Leider nimmt mein Programm beim Starten der Kamera immer nur eine an. Und zwar die, die ich beim letzten mal über das automatisch (von Windows) aufpoppende Fenster ausgewählt habe. (Foto hier: https://picasaweb.google.com/115345283700031324241/Projekt?authkey=Gv1sRgCJOHtoOyk_7nIg#)
Diese Fenster erhalte ich nur, wenn ich es durch eine doppelte Eingabe von
[Handle] = w32.GetCaptureHandle [Handle] = w32.GetCaptureHandle
provoziere. In dem Fall startet dann das Video in der Picturebox, das Fenster poppt auf, ich wähle eine Kamera aus und falls es nicht die bereits ausgewählte Kamera ist, kann ich das Video auch nicht mehr beenden. Durch Neustart und Weglassen des zweiten [Handle] = w32.GetCaptureHandle nimmt er dann automatisch für alle Zukunft die Kamera, die ich ausgewählt habe. Weil ich aber zwischen zwei Kameras hin- und herwechsel geht das so nicht.
Ich habe schon versucht das [Handle] auf Null zu setzen, aber folgende Schritte haben nichts gebracht:
[Handle] = [Handle].Equals(IntPtr.Zero) w32.SetHandle = [Handle].Equals(IntPtr.Zero) w32.MyHandle = IntPtr.Zero
Weiterhin habe ich versucht die CameraID in der Klasse manuell zu verändern. Standart ist 0, eine Änderung bis 10 bringt keinerlei Erfolg.
Für jede Hilfe bin ich sehr dankbar!
Gruß, Sebastian
[VB express 2010]