Hallo,
vor einiger Zeit ist noch mal nach Bildverarbeitung/webcam gefragt worden. Da ich vor Kurzem von VB6 auf vb.net umgestiegen bin, stelle ich hier noch mal meine Erfahrungen und Codeschnipsel zusammen. Vielleicht ist es ja dem einen oder anderen hilfreich Als erstes ein kurzes Video, vom Monitor mitgeschnitten:
Die webcam ist im 2.Stockwerk angebracht. Die Videoüberwachung besteht im wesentlichen aus:
1. Erstellen von bitmaps aus dem laufenden mjpg-stream
2. Korrektur der schrägen, verzerrten Bildern in eine Aufsicht von oben
3. Objekterkennung des Rasenrobo
4. Steuerung des Rasenrobo entlang eines vorgezeichneten Kurses
Für Objekterkennung könnte man auch OpenCV einsetzen. Ginge sicher flotter, wollte mir aber mal alles zu Fuß erarbeiten. Aforge habe ich kurz angetestet. Nachdem es eine gefühlte Ewigkeit dauerte, bis der live-stream auf dem Monitor war, habe ich auch das gelassen und alles nur mit vb.net Bordmittel programmiert.
zu 1.: Habe eine Klassenbibliothek "streamtoimage.dll" gestrickt (in OpenCV gibt´s dafür sicher fertige .dll):
Aus dem Programm können dann laufend mit img=streamtoimage("IPwebcam","Anfrage") Bilder aus dem web-stream geladen werden. Tricky ist der Ausdruck für "Anfrage". Der ist von der webcam abhängig und z.B. für meine Edimax IC-3115W: snapshot.cgi Gibt man z.B. im webbrowserCode:Imports System.Drawing, System.Drawing.ImagingImports System.Net, System.IO Imports System.Net.Sockets Public Class Streamtoimage Shared img As Bitmap, flag As Integer = 0, flag2 As Integer = 0 Shared flag3 As Integer = 0, z As Integer = 0, z5 As String, ii As Integer = 0 Shared tcpclient As TcpClient Shared enc As System.Text.Encoding = System.Text.Encoding.Default Shared st As String Shared buffer5(400000) As Byte, buffer6(200000) As Byte, remotehost2 As String = "", schnipsel2 As String = "" Shared WithEvents t1 As Timers.Timer Public Shared Function zeit() Return z End Function Public Shared Sub starten() If flag3 = 1 Then Exit Sub flag3 = 1 : ii = 0 : flag = 0 : flag2 = -200 tcpclient = New Net.Sockets.TcpClient() tcpclient.ReceiveBufferSize = 100000 tcpclient.Client.Connect(remotehost2, 80) st = "GET /" + schnipsel2 + " HTTP/1.1" + vbCrLf + "Host: 192.168.1.15" + vbCrLf + "Connection: keep-alive" + vbCrLf + vbCrLf ' + "Referer: http://" + "192.168.1.35/live.asp?r=20121005" + vbCrLf + vbCrLf tcpclient.Client.Send(enc.GetBytes(st)) t1 = New Timers.Timer : t1.Interval = 30 : t1.Enabled = True flag3 = 0 End Sub Public Shared Function streamtoimage(ByVal remoteHost As String, ByVal schnipsel As String) remoteHost2 = remoteHost : schnipsel2 = schnipsel If IsNothing(tcpclient) = True Then starten() If tcpclient.Connected = False Then starten() Return (img) End Function Protected Overrides Sub Finalize() t1.Enabled = False tcpclient.Close() MyBase.Finalize() End Sub Private Shared Sub t1_Elapsed(ByVal sender As Object, ByVal e As System.Timers.ElapsedEventArgs) Handles t1.Elapsed If flag > 0 Then Exit Sub flag = 1 : flag2 = flag2 + 1 : If flag2 = 50 Then starten() Dim data As String, i As Integer = 0, iii As Integer Dim länge As Integer, anf As Integer If tcpclient.Client.Available > 0 Then i = tcpclient.Client.Receive(buffer6) If ii + i >= 400000 Then ii = 0 Array.Copy(buffer6, 0, buffer5, ii, i) : ii = ii + i data = enc.GetString(buffer5, 0, ii) iii = InStr(data, "Content-Length") If iii > 0 And ii > iii + 50 Then länge = (Val(Trim(Mid(data, iii + 15, 7)))) anf = InStr(iii + 18, data, vbCrLf + vbCrLf) If anf + 3 + länge <= ii Then Array.Copy(buffer5, anf + 3, buffer6, 0, länge) img = Bitmap.FromStream(New MemoryStream(buffer6)) z = Now.Second * 1000 + Now.Millisecond ii = 0 : flag2 = 0 End If End If End If flag = 0 End Sub End Class
http://IPwebcam/snapshot.cgi
ein, kommt sofort der livestream. Für andere webcam läßt sich der Ausdruck aus dem Quellcode rausfischen.
2. das image packt man in ein array (buffer1):
dann verschiebt man die Pixel im Array buffer um eine Aufsicht von oben zu erhalten. Stichwort Koordinatentransformation,ProjektionCode:Dim bd1 As BitmapData Dim rect As New Rectangle(0, 0, img.Width, img.Height) bd1 = img.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb) dim max as integer = bd1.Stride * bd1.Height - 1 dim buffer1(max) as integer Copy(bd1.Scan0, buffer1, 0, max) : img.UnlockBits(bd1)
Die Unterseite des Videobildes kann man bereits durch Ausrichten der webcam parallel zur x-Achse einstellen. Dann sind noch 3 Parameter einzustellen, die man am besten durch probieren ermittelt. Mit yschräge wird die y-Achse senkrecht zu x-Achse ausgerichtet. a und b entsprechen dem Abstand der Fluchtpunkte (Horizont) in x und y Richtung vom Nullpunkt.Code:matrix() . . . For i = 1 To max : Bildneu(i) = buffer1(matrixxy(i)) : Next . . . Sub matrix() Dim x1 As Integer, y1 As Integer, x2 As Integer, y2 As Integer Dim a As Integer, b As Integer, f As Single, yschräge As Integer a = 1400 : b = 800 : yschräge = 70 For k = 0 To img.Height - 1 : For i = 0 To img.Width - 1 x1 = img.Height - k : y1 = i - yschräge * (x1 / img.Height) '- d f = (a * b - x1 * a - y1 * b) : If f < 1 Then f = 1 f = a * b / f y1 = (f * y1 * 0.5) : x1 = (f * x1 * 0.5) x1 = img.Height - x1 if x1 >= 0 And y1 >= 0 And x1 < img.Height And y1 < img.Width Then For a1 = 0 To 2 : For a2 = 0 To 2 : For a3 = 0 To 2 y2 = y1 + a1 : x2 = x1 + a2 If k*stride + i*3 + a3 <= max And x2*stride + y2*3 + a3 <= max Then matrixxy(x2 * stride + y2 * 3 + a3) = k * stride + i * 3 + a3 End If Next : Next : Next End If Next i : Next k End Sub
3. Erkennung des rasenrobo: Der Robo hat einen roten Fleck. Grob gesagt wird zu jedem Pixel der Wert 3 * rot - blau - grün bestimmt. Dann bestimmt man die größte positive und negative Änderung dieses Parameters von einem Bild zum nächsten und mittelt noch über mehrere benachbarte Pixel. Wer´s genauer wissen will, muss sich leider durch folgenden Code arbeiten:
xmit und ymit sind dann die gesuchten Koordinaten. Anschließend kann man noch evtl. Ausreißer von xmit und ymit verwerfen. Die Werte machen ja keine großen Sprünge.Code:schritt=2 ' um´s etwas zu beschleunigen For k = 0 To img.Height - 1 Step schritt For i = 0 To (img.Width - 1) Step schritt blau = Bildneu(k * stride + i * 3) grün = Bildneu(k * stride + i * 3 + 1) rot = Bildneu(k * stride + i * 3 + 2) blaualt = Bildalt(k * stride + i * 3) grünalt = Bildalt(k * stride + i * 3 + 1) rotalt = Bildalt(k * stride + i * 3 + 2) S5(i, k) = 3 * rot - blau - grün - 3 * rotalt + blaualt + grünalt Next i : Next k Bildneu.CopyTo(Bildalt, 0) 'als nächstes Mittelung mit benachbarten Pixel For k = 1 To img.Height - 1 Step schritt For i = 1 To (img.Width - 1) Step schritt If S5(i, k) <> 0 Then S1(i, k) = S5(i, k) + (S1(i - 1, k) + S1(i, k - 1)) / 9 * 4 Endif ii = img.Width - i - schritt : kk = img.Height - k - schritt If S5(ii, kk) <> 0 Then S2(ii, kk) = S5(ii, kk) + (s2(ii + 1, kk) + s2(ii, kk + 1)) / 9 * 4 endif Next i : Next k ' Maximum- und Minimumwerte suchen For k = 0 To img.Height - 1 Step schritt For i = 0 To (img.Width - 1) Step schritt S1(i, k) = S1(i, k) + s2(i, k) If smax < S1(i, k) Then smax = S1(i, k) : xmax = i : ymax = k If smin > S1(i, k) Then smin = S1(i, k) : xmin = i : ymin = k Next i : Next k If smax < 200 Then xmax = -100 : ymax = -300 'nichts gefunden If smin > -200 Then xmin = -200 : ymin = -400 'nichts gefunden xmit = 0 : ymit = 0 ' nur wenn max und min nahe beieinander liegen auswerten ' Mittelpunkt zwischen Maximum und Minimum ermitteln (Schwerpunkt) If abs(ymax - ymin) < 40 And abs(xmax - xmin) < 40 Then ymit = (ymax + ymin) / 2 : xmit = (xmax + xmin) / 2 If ymit Mod 2 = 1 Then ymit = ymit + 1 If xmit Mod 2 = 1 Then xmit = xmit + 1 x = 0 : y = 0 : m1 = 0 For i = xmit - 30 To xmit + 30 Step schritt For k = ymit - 30 To ymit + 30 Step schritt If i > 0 And k > 0 Then x = x+i*abs(S1(i, k)): y = y+k*abs(S1(i, k)): m = m+abs(S1(i, k)) End If Next : Next xmit = x / m : ymit = y / m End If
Dann braucht man noch etwas an Krimskrams für ne GUI drumrum. Uff, das war´s dann. Von denen die sich mit Objekterkennung auskennen, hätte ich natürlich gerne gewußt, ob´s z.B. mit OpenCV wesentlich besser und einfacher geht.
Beste Grüße
Christian







Zitieren

Lesezeichen