Opencv Buffersize lässt sich nicht einstellen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
bachatero18
User
Beiträge: 41
Registriert: Montag 12. August 2019, 13:57

Hallo Zusammen,

ich hab nach ein paar Tests und Durchläufen gemerkt, dass die Liveübertragung meiner IP Kamera nicht wirklich live ist.

Also wenn ich das richtige sehe hat die IP-Kamera eine bestimmte Anzahl von Bilder die das Pythonscript auch auf nimmt nur eben schön sauber alle nach einander anzeigt.
Wenn ich jetzt aber deutlicher weniger Bilder pro Sekunde bei mir Anzeige wird der Versatz des Bildes immer größer.

Ich hatte jetzt versucht die Buffersize mit 1 anzugeben aber da hat sich nichts verändert. Ein paar Sachen hab ich online schon gelesen hab die aber ehrlich gesagt nicht verstanden.
Hab ihr eine Idee was ich noch machen kann? kann ich den Buffer einfach leeren? oder ihm einfach sagen gibt mir immer das neuste Bild und nicht das älteste?

E

Code: Alles auswählen

def init(top, gui, *args, **kwargs):
    global w, top_level, root, cap
    w = gui
    top_level = top
    root = top
    root.protocol("WM_DELETE_WINDOW", destroy_window)
    root.state("zoomed")
    
    cap = cv2.VideoCapture(Kamera_ID) 
    cap.set(cv2.CAP_PROP_BUFFERSIZE,1)# Hier hat sich nichts geändert, was auffällt ist, dass die beiden folgenden Befehle auch überhaupt keine Auswirkung haben
    cap.set(cv2.CAP_PROP_FRAME_WIDTH, width) 
    cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height)

    label_cam = tk.Label(top) #erstellt ein neues Label im Toplevel
    label_cam.pack()

    def show_frame():
        
        _,frame = cap.read() #
        frame = cv2.resize(frame,(width*1, height*1))#,interpolation=cv2.INTER_CUBIC) #hier nochma neuskalieren weil er es oben nicht richtig macht
        hsv= cv2.cvtColor(frame ,cv2.COLOR_BGR2RGB)
        
        img = Image.fromarray(hsv) #
        imgtk = ImageTk.PhotoImage(image = img) #
        label_cam.imgtk = imgtk #
        label_cam.configure(image=imgtk) #

        root.after(50,show_frame)

    show_frame()
__deets__
User
Beiträge: 14523
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du kannst die frame-acquise in einen Thread auslagern, und dir immer nur den letzten Frame merken, und anzeigen. Das sollte das Problem loesen. Dazu ist aber der Einstieg in die OO-Programmierung empfehlenswert, denn sonst bekommst du die diversen Beteiligten nicht unter Kontrolle. Mit global jedenfalls faengst du dir auf Dauer Probleme ein, weil das zu unuebersichlich wird.
Benutzeravatar
__blackjack__
User
Beiträge: 13074
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@bachatero18: Bitte ``global`` sofort wieder vergessen. Alles was eine Funktion oder Methode ausser Konstanten benötigt, wird als Argument(e) übergeben.

Damit fallen dann die Zuweisungen an `w` und `top_level` weg, wobei `w` auch ein sehr schlechter Name ist.

Dann ist die Zuweisung an `root` unsinnig weil Du nun das gleiche Objekt in der Funktion als `root` und `top` hast, und auch noch beide Namen verwendest. Warum? Was ist das Entscheidungskriterium mal den einen und mal den anderen Namen für das *selbe* Objekt im *selben* Namensraum zu verwenden?

Die Argumente `gui`, `args`, und `kwargs` werden nicht verwendet.

`Kamera_ID` ist falsch geschrieben. Entweder ist das eine Konstante, dann KOMPLETT_GROSS, oder es ist eine variable, dann klein_mit_unterstrichen und dann muss das auch als Argument übergeben werden.

Namen sollten keine kryptischen Abkürzungen enthalten oder gar nur daraus bestehen. `cap` hat als Wort eine Bedeutung („Kappe“), könnte aber beispielsweise auch eine Abkürzung für `capability` sein.

`label_cam` ist falsch herum wenn man nicht Yoda ist. 😎

Funktionen sollte man in der Regel nicht verschachteln, solange die nicht sehr trivial sind oder man die tatsächlich für ein Closure *braucht*.

Mit den mindestens 50 Millisekunden zwischen den `show_frame()`-Aufrufen darf die Kamera maximal 20 Bilder pro Sekunde liefern, sonst gibt's Stau. Wobei man die Aufruffrequenz besser deutlich niedriger ansetzt als die Bildwiederholrate der Kamera, zum Beispiel halb so gross wie nötig. Der `read()`-Aufruf blockiert dann ja entsprechend bis das nächste Bild dann tatsächlich zur Verfügung steht. Also entweder bei `after()` den Wert für die Wartezeit entsprechend der gelieferten Bildrate anpassen, oder die Bildrate an den Wert bei `after()` anpassen. Oder eine Kombination. Also zum Beispiel bei `after()` etwas zwischen 8 und 10 um bei 60 Bildern pro Sekunde auf der sicheren Seite zu sein.

Man sollte das erste Argument im Tupel das `read()` zurückgibt nicht einfach ignorieren. Das geht solange gut bis das mal nicht gut geht und der Code auf die Nase fällt weil `frame` nichts ethält.

`hsv` ist ein verdammt schlechter Name für Bilddaten die gar nicht im HSV- sondern im RGB-Format sind.

Ungetestet:

Code: Alles auswählen

def show_frame(video, width, height, camera_image_label):
    is_ok, frame = video.read()
    if is_ok:
        image = ImageTk.PhotoImage(
            image=Image.fromarray(
                cv2.cvtColor(
                    cv2.resize(frame, (width * 1, height * 1)),
                    cv2.COLOR_BGR2RGB,
                )
            )
        )
        camera_image_label.image = image
        camera_image_label.configure(image=image)

    camera_image_label.after(
        10, show_frame, video, width, height, camera_image_label
    )


def init(window, kamera_id, width, height):
    window.protocol("WM_DELETE_WINDOW", destroy_window)
    window.state("zoomed")

    video = cv2.VideoCapture(kamera_id)
    video.set(cv2.CAP_PROP_FRAME_WIDTH, width)
    video.set(cv2.CAP_PROP_FRAME_HEIGHT, height)

    camera_image_label = tk.Label(window)
    camera_image_label.pack()

    show_frame(video, width, height, camera_image_label)
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
bachatero18
User
Beiträge: 41
Registriert: Montag 12. August 2019, 13:57

Erstmal danke für die Antworten.

Ja klar hast recht ich hab mich soweit an ein alten Programm gehalten, wo ich tatsächlich hsv verwendet habe und paar andere Sachen hast du auch recht. Komme ich so beim Aufbau aber erstmal mit klar.

Aber du hast doch jetzt nur die Bildabfrage verfünffacht das wollte ich ja gar nicht das bekomm ich ja auch hin. Ich wollte das neuste Bild ausm Buffer oder nur ein Bild drin lassen. Aber das Programm jetzt einfach so schnell laufen lassen, dass der Buffer nicht aufgebaut wird ist ja jetzt auch nicht die Lösung. Ich brauch ja keine 100 Bilder die Sekunde es reichen absolut 10 Bilder.
Benutzeravatar
__blackjack__
User
Beiträge: 13074
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@bachatero18: Also erst mal habe ich da nichts an der Bildrate geändert, die kommt ja von der Kamera, und wenn die Bilder bei Dir verzögert kommen, dann ist die halt momentan grösser als die 20 Bilder/Sekunde mit der Dein Code maximal gerade mal so klar gekommen wäre. Meiner käme auch mit 60 Bildern/Sekunde klar. Wenn Du nur 10 brauchst, musst Du das der Kamera sagen, dass die nur 10 liefern soll und nicht wie viel auch immer die per Voreinstellung liefert. Und dann ginge der Wert von 50 Millisekunden bei `after()` auch klar.

Falls die Kamera nicht auf die FPS-Einstellung von OpenCV reagiert, Du also die Bildwiederholrate nicht beeinflussen kannst — solche Kombinationen von Kamera-Hardware und Treiber/Backend gibt es wohl — dann müsstest Du das tatsächlich in einen Thread auslagern und eben so viel/schnell lesen wie die Kamera Bilder liefert und einfach Bilder verwerfen. Beispielsweise eine Queue mit einer Maximallänge von eins zwischen die beiden Threads hängen.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
bachatero18
User
Beiträge: 41
Registriert: Montag 12. August 2019, 13:57

Bildrate nein aber Bildabfrage aus dem Buffer hattest du ja verfünffacht durch die Änderung von 50ms auf 10ms. Kamera kann ich bestimmt auch ändern aber das sollte nicht mein Lösungsansatz sein.

Ich habe eben tatsächlich den Befehl ' cap.grab() ' gefunden, damit löscht er den Buffer der soll laut einem anderen Beitrag nicht immer funktionieren aber bisher ist er zuverlässig bei mir.
Benutzeravatar
__blackjack__
User
Beiträge: 13074
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@bachatero18: Ich weiss nicht ob Dir die Zusammenhänge so ganz klar sind: Doch Du musst bei der Kamera bestimmen wie viel FPS die verwenden soll. Die FPS werden bei der Kamera festgelegt, nicht dadurch wie oft pro Sekunde man `read()` aufruft. Wenn man 100 mal die Sekunde `read()` aufruft bekommt man dadurch nicht 100 FPS geliefert. Beziehungsweise kann man wenn die Kamera beispielsweise 60 FPS liefert gar nicht 100 mal in der Sekunde `read()` aufrufen, weil dieser Aufruf ja *blockiert* und das abrufen damit automatisch auf die 60 FPS drosselt. Die Wartezeit bei `after()` muss nur kurz genug sein, damit sich keine Bilder aufstauen. Andererseits muss man auch aufpassen die Wartezeit nicht *zu* kurz zu machen, weil dann unnötig Zeit von der GUI im blockierenden `read()`-Aufruf verschwendet wird.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Antworten