Seite 1 von 1
Aus Schleife ausbrechen
Verfasst: Donnerstag 20. Oktober 2016, 17:01
von Popkultur
Hallo!
Ich weiß nicht, ob der folgende Code ausreicht, aber er schmeisst eine nichtssagende Fehlermeldung im Programm, Frage: Kann man das formal nicht so schreiben mit dem return und break am Ende innerhalb einer Klasse? Der Code liest einen Stream und gibt ein Bild zurück.
Code: Alles auswählen
def get_frame(self):
response = urllib.request.urlopen(self.req)
while True:
self.bytes+=response.read(1024)
x = self.bytes.find(b'\xff\xd8')
y = self.bytes.find(b'\xff\xd9')
if x!=-1 and y!=-1:
jpg = self.bytes[x:y+2]
self.bytes = self.bytes[x+y:]
i = cv2.imdecode(np.fromstring(jpg, dtype=np.uint8),1)
return i
break
Re: Aus Schleife ausbrechen
Verfasst: Donnerstag 20. Oktober 2016, 17:17
von snafu
Das `break` ist überflüssig. Die Schleife wird bereits durch das `return` verlassen.
Re: Aus Schleife ausbrechen
Verfasst: Donnerstag 20. Oktober 2016, 19:37
von BlackJack
@Popkultur: Was ist denn die nichtssagende Fehlermeldung? Und wo genau tritt die auf? Also am besten den kompletten Traceback 1:1 zeigen.
Ich denke es ist auch fehlerhaft, weil da beim erneuten Aufruf noch Informationen vom letzten Aufruf vorhanden sein können, die zu kaputten JPGs führen können.
Re: Aus Schleife ausbrechen
Verfasst: Freitag 21. Oktober 2016, 07:46
von Sirius3
@Popkultur: willst Du von dem Stream immer nur das erste Bild? Das mit den self.bytes ist auf jeden Fall falsch.
Wenn man von einer URL einfach ein JPG lesen will, dann ist die Klasse überflüssig:
Code: Alles auswählen
def get_frame(url):
response = urllib.request.urlopen(url)
bytes = b""
while True:
data = response.read(1024)
if not data:
raise IOError("end of stream")
bytes += data
x = bytes.find(b'\xff\xd8')
y = bytes.find(b'\xff\xd9')
if x!=-1 and y!=-1:
break
return cv2.imdecode(np.fromstring(bytes[x:y+2], dtype=np.uint8), 1)
Will man von einer URL viele Frames lesen, dann ist die Klasse ebenso überflüssig und man würde eher einen Generator schreiben:
Code: Alles auswählen
def iter_frame(url):
response = urllib.request.urlopen(url)
bytes = b""
while True:
data = response.read(1024)
if not data:
break
bytes += data
x = bytes.find(b'\xff\xd8')
y = bytes.find(b'\xff\xd9')
if x!=-1 and y!=-1:
yield cv2.imdecode(np.fromstring(bytes[x:y+2], dtype=np.uint8), 1)
bytes = bytes[y+2:]
Aus der opencv-Dokumentation bin ich nicht schlau geworden, ob das Umwandeln in ein numpy-Array überhaupt notwendig ist, oder imdecode direkt einen Bytestring schluckt. Irgendwie scheinen alle sonstigen Beispiele nur voneinander zu kopieren.
Re: Aus Schleife ausbrechen
Verfasst: Freitag 21. Oktober 2016, 12:25
von Popkultur
Vielen Dank! Ich hatte wohl python 2 Code mit python 3 versucht zu vermischen, weshalb es nicht gleich funktioniert hat. Sirius3 hat mit seinem letzten Codeschnippsel den Nagel auf den Kopf getroffen, auch bei mir funktioniert es mittlerweile.
Re: Aus Schleife ausbrechen
Verfasst: Freitag 21. Oktober 2016, 17:05
von snafu
Falls das Bild nicht komplett in einen Chunk passt:
Code: Alles auswählen
def get_image(stream, chunksize=1024):
start = stop = -1
while True:
data = stream.read(chunksize)
if not data:
raise IOError('end of stream')
if start < 0:
start = data.find(b'\xff\xd8')
if start >= 0:
stop = data.find(b'\xff\xd9')
if stop >= 0:
buf = np.fromstring(data[start:stop + 2], dtype=np.uint8)
return cv2.imdecode(buf, 1)
Re: Aus Schleife ausbrechen
Verfasst: Freitag 21. Oktober 2016, 17:39
von snafu
Mein vorheriger Code hat den Aspekt, dass man Inhalt, der über mehrere Chunks geht, natürlich speichern sollte, gar nicht enthalten. Somit wäre der für diesen Fall auch nicht sinnvoll einsetzbar.
Hier die verbesserte Version:
Code: Alles auswählen
def get_image(stream, chunksize=1024):
chunks = []
start = stop = -1
while stop < 0:
chunk = stream.read(chunksize)
if not chunk:
raise IOError('end of stream')
start = chunk.find(b'\xff\xd8')
if start < 0:
if not buf:
# Try next chunk
continue
start = 0
stop = chunk.find(b'\xff\xd9')
if stop < 0:
stop = len(chunk)
chunks.append(chunk[start:stop + 2])
buf = np.fromstring(''.join(chunks), dtype=np.uint8)
return cv2.imdecode(buf, 1)
Re: Aus Schleife ausbrechen
Verfasst: Freitag 21. Oktober 2016, 18:12
von Sirius3
@snafu: das wird leider nicht besser. buf ist in Zeile 10 nicht definiert. Schwieriger zu entdecken ist der Bug, wenn im ersten Chunk das letzte Byte '\xff' ist, und erst im nächsten Chunk '\xd8' oder '\xd9' gelesen wird.
Re: Aus Schleife ausbrechen
Verfasst: Freitag 21. Oktober 2016, 18:41
von snafu
Jetzt ist der `NameError` weg. Außerdem habe ich die Schleifenbedingung korrigiert.
Code: Alles auswählen
def get_image(stream, chunksize=1024):
chunks = []
stop_seen = False
while not stop_seen:
chunk = stream.read(chunksize)
if not chunk:
raise IOError('end of stream')
start = chunk.find(b'\xff\xd8')
if start < 0:
if not chunks:
# Try next chunk
continue
start = 0
stop = chunk.find(b'\xff\xd9')
if stop < 0:
stop = len(chunk)
else:
stop_seen = True
chunks.append(chunk[start:stop + 2])
buf = np.fromstring(''.join(chunks), dtype=np.uint8)
return cv2.imdecode(buf, 1)
Re: Aus Schleife ausbrechen
Verfasst: Freitag 21. Oktober 2016, 20:42
von BlackJack
@snafu: Du hast immer noch das Problem das die Markierungen über `chunk`-Grenzen hinweg gehen können.
Re: Aus Schleife ausbrechen
Verfasst: Freitag 21. Oktober 2016, 23:52
von snafu
Dann hier nochmal abgeändert:
Code: Alles auswählen
REMAINING = 'remaining'
PROCESSED = 'processed'
def get_chars(stream, needle, buffer=None, chunksize=1024, mode=REMAINING):
"""
Read chunks from stream and stop when needle was found.
Return needle and all characters after it if mode is 'remaining'.
Return all characters before needle and needle if mode is 'processed'.
Note that no more chunks are read when needle was found. Therefore,
characters might remain untouched at the stream. These characters are
not included in the result.
"""
if mode not in (REMAINING, PROCESSED):
raise ValueError('unknown mode')
empty = type(needle)()
if not buffer:
buffer = empty
processed = []
chunks = iter(lambda: stream.read(chunksize), empty)
for chunk in chunks:
haystack = buffer + chunk
index = haystack.find(needle)
if index >= 0:
if mode == PROCESSED:
buffered = haystack[:index + len(needle)]
return empty.join(processed) + buffered
return haystack[index:]
buffer = haystack[-len(needle):]
if mode == PROCESSED:
data = haystack[:-len(needle)]
processed.append(data)
return empty
def get_image_data(stream, start_mark=b'\xff\xd8', stop_mark=b'\xff\xd9'):
data = get_chars(stream, start_mark, mode=REMAINING)
if not data:
raise IOError('missing start mark')
buf = data[len(start_mark):]
data = get_chars(stream, stop_mark, buffer=buf, mode=PROCESSED)
if not data:
raise IOError('missing stop mark')
return data
Re: Aus Schleife ausbrechen
Verfasst: Samstag 22. Oktober 2016, 00:52
von snafu
Hier nochmal etwas robuster.
Die `get_chars()`-Funktion mit ihren verschiedenen Modi ist recht komplex geworden. Außerdem ist sie nicht bis zum Ende gedacht, da sie im Modus 'remaining' Zeichen verliert, die im zuletzt gelesenen Chunk hinter der Markierung stehen, da ich ja beim Auftreten der Markierung stoppe. Da könnte man die Definition verändern, hätte dann aber das Problem, dass der Aufrufer beim erhaltenen String nochmal prüfen müsste, ob da am Ende Zeichen übrig sind, die er nicht gebrauchen kann. Ein dritter Modus wäre hier denkbar, aber das hab ich einfach mal außen vor gelassen und nicht mehr eingebaut. Möglicherweise (oder: hoffentlich) kann man das alles sowieso mit weniger Code programmieren...
Re: Aus Schleife ausbrechen
Verfasst: Samstag 22. Oktober 2016, 17:45
von Sirius3
@snafu: Deine Funktion hat jetzt noch Probleme, falls die Endmarkierung im selben Chunk wie die Startmarkierung steht, und das der letzte Chunk war. Außerdem ist die Funktion sehr speziell, mehrere Blöcke mit Endmarkierung kann man nicht lesen.
Eigentlich ist das Problem das selbe wie bei jeder readline-Funktion, nur dass das Zeileendezeichen variabel ist. Dazu braucht man eine Klasse, um die zu viel gelesenen Bytes irgendwo zu speichern:
Code: Alles auswählen
from functools import partial
import numpy
import cv2
class BlockReader(object):
def __init__(self, stream, chunksize=1024):
self.stream = stream
self.chunksize = chunksize
self.buffer = b""
def read_until(self, end_of_block, include_marker=True, discard=False):
result = []
buffer = self.buffer
read = partial(self.stream.read, self.chunksize)
while True:
idx = buffer.find(end_of_block)
if idx >= 0:
break
data = read()
if not data:
raise IOError("%s not found" % end_of_block)
if not discard:
result.append(buffer[:-len(end_of_block)+1])
buffer = buffer[-len(end_of_block) + 1:] + data
if include_marker:
idx += len(end_of_block)
self.buffer = buffer[idx:]
if discard:
return None
result.append(buffer[:idx])
return b"".join(result)
def read_image(stream):
reader = BlockReader(stream)
reader.read_until(b'\xff\xd8', False, True)
data = reader.read_until(b'\xff\xd9')
buf = np.fromstring(data, dtype=np.uint8)
return cv2.imdecode(buf, 1)