Wie Sie effizient Lesen und speichern von video von einer IP-Kamera?
Habe ich ein python-script, das ich verwenden, um schnappen Sie Bilder von einer ip-Kamera über mein home-Netzwerk und fügen Sie Datum-Zeit-Informationen. In einem 12-Stunden-Frist greift es sich rund 200.000 Bilder. Aber wenn zoneminder (Kamera-überwachung-software) auf der Kamera verwaltet 250.000 in einem Zeitraum von 7 Stunden.
Ich Frage mich, wenn jemand helfen könnte mich zu verbessern mein Skript Effizienz habe ich versucht, mit der threading-Modul zu erstellen, 2 threads, aber es hat nicht geholfen, ich bin mir nicht sicher, ob ich es umgesetzt haben, ist es falsch oder nicht. Unten ist der code, den ich momentan verwende:
#!/usr/bin/env python
# My First python script to grab images from an ip camera
import requests
import time
import urllib2
import sys
import os
import PIL
from PIL import ImageFont
from PIL import Image
from PIL import ImageDraw
import datetime
from datetime import datetime
import threading
timecount = 43200
lock = threading.Lock()
wdir = "/workdir/"
y = len([f for f in os.listdir(wdir)
if f.startswith('Cam1') and os.path.isfile(os.path.join(wdir, f))])
def looper(timeCount):
global y
start = time.time()
keepLooping = True
while keepLooping:
with lock:
y += 1
now = datetime.now()
dte = str(now.day) + ":" + str(now.month) + ":" + str(now.year)
dte1 = str(now.hour) + ":" + str(now.minute) + ":" + str(now.second) + "." + str(now.microsecond)
cname = "Cam1:"
dnow = """Date: %s """ % (dte)
dnow1 = """Time: %s""" % (dte1)
buffer = urllib2.urlopen('http://(ip address)/snapshot.cgi?user=uname&pwd=password').read()
img = str(wdir) + "Cam1-" + str('%010d' % y) + ".jpg"
f = open(img, 'wb')
f.write(buffer)
f.close()
if time.time()-start > timeCount:
keepLooping = False
font = ImageFont.truetype("/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf",10)
img=Image.open(img)
draw = ImageDraw.Draw(img)
draw.text((0, 0),cname,fill="white",font=font)
draw.text((0, 10),dnow,fill="white",font=font)
draw.text((0, 20),dnow1,fill="white",font=font)
draw = ImageDraw.Draw(img)
draw = ImageDraw.Draw(img)
img.save(str(wdir) + "Cam1-" + str('%010d' % y) + ".jpg")
for i in range(2):
thread = threading.Thread(target=looper,args=(timecount,))
thread.start()
thread.join()
wie könnte ich das verbessern, dieses Skript oder wie öffne ich einen stream von der Kamera, dann schnappen Sie Bilder aus dem stream? würde, dass auch die Steigerung der Effizienz /capture-rate?
Edit:
Dank kobejohn Hilfe ich habe mit der folgenden Umsetzung. laufen für einen 12-Stunden-Zeitraum, es ist mittlerweile über 420.000 Bilder aus 2 separaten Kameras (zur gleichen Zeit), von denen jedes für Ihre eigenen thread in der gleichen Zeit, im Vergleich zu etwa 200.000 von meine ursprüngliche Implementierung vor. Der folgende code wird ausgeführt 2 Kamera parallel (oder nahe genug), und fügen Sie text zu Ihnen:
import base64
from datetime import datetime
import httplib
import io
import os
import time
from PIL import ImageFont
from PIL import Image
from PIL import ImageDraw
import multiprocessing
wdir = "/workdir/"
stream_urlA = '192.168.3.21'
stream_urlB = '192.168.3.23'
usernameA = ''
usernameB = ''
password = ''
y = sum(1 for f in os.listdir(wdir) if f.startswith('CamA') and os.path.isfile(os.path.join(wdir, f)))
x = sum(1 for f in os.listdir(wdir) if f.startswith('CamB') and os.path.isfile(os.path.join(wdir, f)))
def main():
time_count = 43200
# time_count = 1
procs = list()
for i in range(1):
p = multiprocessing.Process(target=CameraA, args=(time_count, y,))
q = multiprocessing.Process(target=CameraB, args=(time_count, x,))
procs.append(p)
procs.append(q)
p.start()
q.start()
for p in procs:
p.join()
def CameraA(time_count, y):
y = y
h = httplib.HTTP(stream_urlA)
h.putrequest('GET', '/videostream.cgi')
h.putheader('Authorization', 'Basic %s' % base64.encodestring('%s:%s' % (usernameA, password))[:-1])
h.endheaders()
errcode, errmsg, headers = h.getreply()
stream_file = h.getfile()
start = time.time()
end = start + time_count
while time.time() <= end:
y += 1
now = datetime.now()
dte = str(now.day) + "-" + str(now.month) + "-" + str(now.year)
dte1 = str(now.hour) + ":" + str(now.minute) + ":" + str(now.second) + "." + str(now.microsecond)
cname = "Cam#: CamA"
dnow = """Date: %s """ % dte
dnow1 = """Time: %s""" % dte1
# your camera may have a different streaming format
# but I think you can figure it out from the debug style below
source_name = stream_file.readline() # '--ipcamera'
content_type = stream_file.readline() # 'Content-Type: image/jpeg'
content_length = stream_file.readline() # 'Content-Length: 19565'
#print 'confirm/adjust content (source?): ' + source_name
#print 'confirm/adjust content (type?): ' + content_type
#print 'confirm/adjust content (length?): ' + content_length
# find the beginning of the jpeg data BEFORE pulling the jpeg framesize
# there must be a more efficient way, but hopefully this is not too bad
b1 = b2 = b''
while True:
b1 = stream_file.read(1)
while b1 != chr(0xff):
b1 = stream_file.read(1)
b2 = stream_file.read(1)
if b2 == chr(0xd8):
break
# pull the jpeg data
framesize = int(content_length[16:])
jpeg_stripped = b''.join((b1, b2, stream_file.read(framesize - 2)))
# throw away the remaining stream data. Sorry I have no idea what it is
junk_for_now = stream_file.readline()
# convert directly to an Image instead of saving /reopening
# thanks to SO: http://stackoverflow.com/a/12020860/377366
image_as_file = io.BytesIO(jpeg_stripped)
image_as_pil = Image.open(image_as_file)
draw = ImageDraw.Draw(image_as_pil)
draw.text((0, 0), cname, fill="white")
draw.text((0, 10), dnow, fill="white")
draw.text((0, 20), dnow1, fill="white")
img_name = "CamA-" + str('%010d' % y) + ".jpg"
img_path = os.path.join(wdir, img_name)
image_as_pil.save(img_path)
def CameraB(time_count, x):
x = x
h = httplib.HTTP(stream_urlB)
h.putrequest('GET', '/videostream.cgi')
h.putheader('Authorization', 'Basic %s' % base64.encodestring('%s:%s' % (usernameB, password))[:-1])
h.endheaders()
errcode, errmsg, headers = h.getreply()
stream_file = h.getfile()
start = time.time()
end = start + time_count
while time.time() <= end:
x += 1
now = datetime.now()
dte = str(now.day) + "-" + str(now.month) + "-" + str(now.year)
dte1 = str(now.hour) + ":" + str(now.minute) + ":" + str(now.second) + "." + str(now.microsecond)
cname = "Cam#: CamB"
dnow = """Date: %s """ % dte
dnow1 = """Time: %s""" % dte1
# your camera may have a different streaming format
# but I think you can figure it out from the debug style below
source_name = stream_file.readline() # '--ipcamera'
content_type = stream_file.readline() # 'Content-Type: image/jpeg'
content_length = stream_file.readline() # 'Content-Length: 19565'
#print 'confirm/adjust content (source?): ' + source_name
#print 'confirm/adjust content (type?): ' + content_type
#print 'confirm/adjust content (length?): ' + content_length
# find the beginning of the jpeg data BEFORE pulling the jpeg framesize
# there must be a more efficient way, but hopefully this is not too bad
b1 = b2 = b''
while True:
b1 = stream_file.read(1)
while b1 != chr(0xff):
b1 = stream_file.read(1)
b2 = stream_file.read(1)
if b2 == chr(0xd8):
break
# pull the jpeg data
framesize = int(content_length[16:])
jpeg_stripped = b''.join((b1, b2, stream_file.read(framesize - 2)))
# throw away the remaining stream data. Sorry I have no idea what it is
junk_for_now = stream_file.readline()
# convert directly to an Image instead of saving /reopening
# thanks to SO: http://stackoverflow.com/a/12020860/377366
image_as_file = io.BytesIO(jpeg_stripped)
image_as_pil = Image.open(image_as_file)
draw = ImageDraw.Draw(image_as_pil)
draw.text((0, 0), cname, fill="white")
draw.text((0, 10), dnow, fill="white")
draw.text((0, 20), dnow1, fill="white")
img_name = "CamB-" + str('%010d' % x) + ".jpg"
img_path = os.path.join(wdir, img_name)
image_as_pil.save(img_path)
if __name__ == '__main__':
main()
BEARBEITEN (26/05/2014):
Ich verbrachte den besseren Teil von 2 Monaten zu aktualisieren versuchen das script /Programm für die Arbeit mit python 3, aber war völlig unfähig, es zu bekommen, etwas zu tun. würde jeder in der Lage sein zu zeigen Sie mich in die richtige Richtung?
Habe ich versucht das 2to3-Skript, aber es wurde nur ein paar Einträge und ich war noch nicht in der Lage, es zu bekommen, um überhaupt funktionieren kann.
sum(1 for f in os.listdir(wdir) if f.startswith('CamFront') and os.path.isfile(os.path.join(wdir, f)))
gut, das Teil ist nur zu prüfen, wenn es die Bilder schon im Arbeitsverzeichnis, um herauszufinden, ob der Zähler beginnt bei 1 oder unter einer anderen Nummer. es ist mehr die rate der Erfassung ich versuche mich zu verbessern in der looper-Funktion. und für die Verbesserung areyou sagen, ersetzen Sie den gesamten y = Teil nur mit y = Summe(1 für f in os.listdir(wdir eingesetzt) falls f ein.startswith('CamFront') ?
Ich bin auch neue Python-Anfänger. Gerade lese ich einige, wo
sun(genrator expression)
besser ist, dann len([listcompresion])
. Natürlich ist dies nicht die Antwort auf Ihre Frage. Ich wünschte, ich könnte, aber in diesem Stadium ich bin nicht in der Lage zu tragen 🙁 🙁Die wichtigste Frage ist - was ist derzeit die meisten Ressourcen. Ist es schlapp auf system calls? Oder Vernetzung? Oder Festplatte? Oder CPU? Definitionen Ihrer hardware und wie ist die Last auf jedem Stück ist es viel hilfreicher, als nur zu zeigen, uns etwas code, ohne jeden Versuch einer Profilierung der Frage und der Hoffnung, dass jemand es für Sie tun.
nun, ich glaube nicht, dass die Festplatte oder die cpu das problem sein würde, wie es läuft auf einem i7 930 cpu, ubuntu-server-os auf einer pcie-ssd, die Bilder gehen an einen sata3-hdd. wie für die cpu-Last nie scheint sehr hoch zu sein, wenn das Skript ausgeführt wird, die ich gerade lief es für 30 Sekunden und die höchste cpu-Auslastung lag bei 18% auf 1 Kern beim greifen von 2 Kamera.
InformationsquelleAutor |
Du musst angemeldet sein, um einen Kommentar abzugeben.
*edit ich zuvor beschuldigt GIL für das Verhalten an, das war dumm. Dies ist ein I/O-bound Prozess, nicht einen CPU-gebundenen Prozess. So multiprocessing ist nicht eine sinnvolle Lösung.
*update fand ich endlich eine demo-ip-Kamera mit den gleichen streaming-Schnittstelle wie deine (glaube ich). Über das streaming-interface, es wird nur eine Verbindung einmal, und liest dann aus dem stream von Daten, als ob es eine Datei um extrahieren von jpg-Bild-frames. Mit dem code unten, packte ich für 2 Sekunden ==> 27 frames, die ich glaube, extrapoliert auf über 300k Bilder in einem Zeitraum von 7 Stunden.
Wenn Sie wollen, mehr noch, Sie bewegen würde, die Bild-änderungen und schreiben in eine Datei auf einem separaten thread und ein Arbeitnehmer tun, dass während der Haupt-thread packt nur aus dem stream, und sendet jpeg-Daten auf die Arbeiter.
*jpg-capture unten scheint nicht schnell genug, das ist logisch. so viele http-Anforderungen zu langsam für alles.
Recht. dumm mich. wenn Sie mitmachen, dann ist es warten, bis die erste fertig ist, bevor die zweite. verschieben Sie die Verknüpfungen aus. Ich habe aktualisiert, der code.
ich denke, dass könnte die Arbeit, die Sie fertig stellen etwa 0,2 von einem zweiten abgesehen jetzt intead der vollen 10-der zweite Probelauf auseinander, die meisten und fast genau zur gleichen Zeit auf einen 60-Sekunden-Testversion. wird, zu geben, wenn ich, fügen Sie eine zweite Kamera loop in den mix wie mit 4 threads 2 für jede Kamera? oder muss ich etwas tun, wie erstellen des Prozesses in der gleichen Weise für sagen wir q anstelle von p? @kobejohn
Davon ausgehend, dass alle Bibliotheken, die Sie verwenden, spielen Sie gut mit multiprocessing, dann mehrere Prozesse, mehrere Kameras funktionieren auf die gleiche wie oben.
habe gerade versucht es ja, es funktioniert, wenn ich, fügen Sie eine zweite Schleife und verwenden Sie die q anstelle von p zu erstellen Prozess. jetzt zu versuchen, sich dem, was Paulus sujested unten zu arbeiten und den text hinzufügen, in nur 1 schreiben.
InformationsquelleAutor KobeJohn
Die Geschwindigkeit, die Sie bekommen für die Umsetzung gegeben habe, sind nicht schlecht.
Sie über das schreiben von 4,5 Bildern pro Sekunde (fps), und zoneminder ist das schreiben fast 10 fps. Unten ist die flow-Diagramm mit ein paar Kommentare um die Dinge zu beschleunigen
ButtzyB: ich bin nicht so bewusst Dinge in den Standard-Bibliotheken. Haben Sie erwägen den Bau einer Zeichenfolge mit allen Zeitstempel info und einen Aufruf zum zeichnen.text?
Ich habe versucht, aber jede Weise, die ich habe versucht, endet mit einer Zeile text, getrennt durch ein weißes Feld. ich denke, kobejohn lief in das gleiche Problem versucht, um eine einzelne Zeichenfolge und hilft mir zusammen mit der neuen Implementierung. @Paul
InformationsquelleAutor Paul
Gibt es ein paar Dinge, die helfen könnten.
Heben Sie die schriftart-öffnen aus dem Funktion, um den Haupt-code, dann geben Sie in das font-Objekt (Eröffnung einer schriftart ist wahrscheinlich nicht trivial in der Zeit, indem Sie es einmal, Sie sind nicht die Zeit nehmen, die Treffer für jedes Bild; Sie noch nie die schriftart ändern on the fly, so teilen die gleiche font-Objekt sollte OK sein).
Können Sie wahrscheinlich Schrott zwei der drei Linien, die enthalten:
draw = ImageDraw.Draw(img)
InformationsquelleAutor Vatine