Ich bin ein großer Fan von interaktiven Visualisierungen. Als Laptop -Imaginative and prescient -Ingenieur befasse ich visuelles Suggestions Entscheidungen treffen. Denken wir an eine sehr einfache Bildverarbeitungspipeline mit einem einzigen Schritt, der einige Parameter enthält, um ein Bild zu transformieren:

Probenverarbeitungspipeline mit fehlender Visualisierung der Ausgabe

Woher wissen Sie, welche Parameter Sie anpassen sollen? Funktioniert die Pipeline überhaupt wie erwartet? Ohne die Visualisierung Ihrer Ausgabe zu finden, können Sie einige wichtige Erkenntnisse verpassen und suboptimale Entscheidungen treffen.

Manchmal können einfach das Ausgangsbild und/oder einige berechnete Metriken angezeigt werden, um die Parameter zu iterieren. Aber ich habe mich in vielen Situationen gefunden, in denen ein Werkzeug in meiner Pipeline schnell und interaktiv hilfreich sein würde, um schnell und interaktiv zu iterieren. In diesem Artikel werde ich Ihnen additionally zeigen, wie Sie mit einfachen integrierten interaktiven Elementen aus arbeiten können OpenCV sowie wie man modernere Benutzeroberflächen für Laptop -Imaginative and prescient -Projekte mithilfe erstellt customtkinter.

Voraussetzungen

Wenn Sie mitmachen möchten, empfehle ich Ihnen, Ihre lokale Umgebung mit der Einrichtung zu bringen UV und installieren Sie die folgenden Pakete:

uv add numpy opencv-Python pillow customtkinter

Ziel

Bevor wir uns mit dem Code des Projekts eintauchen, lassen Sie uns schnell beschreiben, was wir erstellen möchten. Die Anwendung sollte den Webcam -Feed verwenden und dem Benutzer ermöglichen, verschiedene Arten von Filtern auszuwählen, die auf den Stream angewendet werden. Das verarbeitete Bild sollte in Echtzeit im Fenster angezeigt werden. Eine grobe Skizze einer potenziellen Benutzeroberfläche würde wie folgt aussehen:

Opencv – GUI

Beginnen wir mit einer einfachen Schleife, die Frames von Ihrer Webcam abruft und in einem OpenCV -Fenster anzeigt.

import cv2

cap = cv2.VideoCapture(0)

whereas True:
    ret, body = cap.learn()
    if not ret:
        break

    cv2.imshow("Video Feed", body)
    
    key = cv2.waitKey(1) & 0xFF
    if key == ord('q'):
        break

cap.launch()
cv2.destroyAllWindows()

Tastatureingang

Die einfachste Möglichkeit, hier Interaktivität hinzuzufügen, besteht darin, Tastatureingänge hinzuzufügen. Zum Beispiel können wir mit den Zahlenschlüssel verschiedene Filter durchlaufen.

...

filter_type = "regular"

whereas True:
    ...

    if filter_type == "grayscale":
        body = cv2.cvtColor(body, cv2.COLOR_BGR2GRAY)
    elif filter_type == "regular":
        move

    ...

    if key == ord('1'):
        filter_type = "regular"
    if key == ord('2'):
        filter_type = "grayscale"
        
    ...

Jetzt können Sie zwischen dem normalen Bild und der Graustufenversion wechseln, indem Sie die Zahlenschlüsseln 1 und 2 drücken. Fügen Sie dem Bild auch schnell eine Bildunterschrift hinzu, damit wir den Namen des Filters, den wir anwenden, tatsächlich sehen können.

Jetzt müssen wir hier vorsichtig sein: Wenn Sie sich die Kind des Rahmens nach dem Filter ansehen, werden Sie feststellen, dass sich die Dimensionalität des Body -Arrays geändert hat. Denken Sie daran, dass OpenCV -Bildarrays bestellt werden HWC (Höhe, Breite, Farbe) mit Farbe als BGR (grün, blau, rot), additionally hat das 640 × 480 -Bild von meiner Webcam Kind (480, 640, 3).

print(filter_type, body.form)
# regular (480, 640, 3)
# grayscale (480, 640)

Da der Graustufenbetrieb ein einzelnes Kanalbild ausgibt, wird die Farbdimension fallen gelassen. Wenn wir jetzt über dieses Bild zeichnen möchten, müssen wir entweder eine einzelne Kanalfarbe für das Graustufenbild angeben oder dieses Bild wieder in das Unique konvertieren BGR Format. Die zweite Choice ist etwas sauberer, da wir die Annotation des Bildes vereinen können.

if filter_type == "grayscale":
    body = cv2.cvtColor(body, cv2.COLOR_BGR2GRAY)
elif filter_type == "regular":
    move

if len(body.form) == 2: # Convert grayscale to BGR
    body = cv2.cvtColor(body, cv2.COLOR_GRAY2BGR)

Untertitel

Ich möchte am unteren Rand des Bildes einen schwarzen Rand hinzufügen, auf dem der Identify des Filters angezeigt wird. Wir können das nutzen copyMakeBorder Funktionieren Sie das Bild mit einer Randfarbe unten. Dann können wir den Textual content über diesen Grenze hinzufügen.

# Add a black border on the backside of the body
border_height = 50
border_color = (0, 0, 0)
body = cv2.copyMakeBorder(body, 0, border_height, 0, 0, cv2.BORDER_CONSTANT, worth=border_color)

# Present the filter identify
cv2.putText(
    body,
    filter_type,
    (body.form(1) // 2 - 50, body.form(0) - border_height // 2 + 10),
    cv2.FONT_HERSHEY_SIMPLEX,
    1,
    (255, 255, 255),
    2,
    cv2.LINE_AA,
)

So sollte der Ausgang aussehen, und Sie können zwischen dem Regular- und Graustufenmodus wechseln und die Rahmen entsprechend beschrieben werden.

Schieberegler

Anstatt die Tastatur als Eingabemethode zu verwenden, bietet OpenCV ein grundlegendes TrackBar Slider UI -Component. Die Trackbar muss zu Beginn des Skripts initialisiert werden. Wir müssen auf das gleiche Fenster verweisen, wie wir unsere Bilder später anzeigen werden, damit ich eine Variable für den Namen des Fensters erstelle. Mit diesem Namen können wir die TrackBar erstellen und sie ein Selektor für den Index in der Liste der Filter sein.

filter_types = ("regular", "grayscale")

win_name = "Webcam Stream"
cv2.namedWindow(win_name)

tb_filter = "Filter"
# def createTrackbar(trackbarName: str, windowName: str, worth: int, depend: int, onChange: _typing.Callable((int), None)) -> None: ...
cv2.createTrackbar(
    tb_filter,
    win_name,
    0,
    len(filter_types) - 1,
    lambda _: None,
)

Beachten Sie, wie wir ein leeres Lambda für die verwenden onChange Rückruf, wir holen den Wert manuell in der Schleife. Alles andere wird gleich bleiben.

whereas True:
    ...

    # Get the chosen filter sort
    filter_id = cv2.getTrackbarPos(tb_filter, win_name)
    filter_type = filter_types(filter_id)

    ...

Und voilà, wir haben eine Trackbar, um unseren Filter auszuwählen.

Jetzt können wir auch problemlos mehr Filter hinzufügen, indem wir unsere Liste erweitern und jeden Verarbeitungsschritt implementieren.

filter_types = (
    "regular",
    "grayscale",
    "blur",
    "threshold",
    "canny",
    "sobel",
    "laplacian",
)

...

    if filter_type == "grayscale":
        body = cv2.cvtColor(body, cv2.COLOR_BGR2GRAY)
    elif filter_type == "blur":
        body = cv2.GaussianBlur(body, ksize=(15, 15), sigmaX=0)
    elif filter_type == "threshold":
        grey = cv2.cvtColor(body, cv2.COLOR_BGR2GRAY)
        _, thresholded_frame = cv2.threshold(grey, thresh=127, maxval=255, sort=cv2.THRESH_BINARY)
    elif filter_type == "canny":
        body = cv2.Canny(body, threshold1=100, threshold2=200)
    elif filter_type == "sobel":
        body = cv2.Sobel(body, ddepth=cv2.CV_64F, dx=1, dy=0, ksize=5)
    elif filter_type == "laplacian":
        body = cv2.Laplacian(body, ddepth=cv2.CV_64F)
    elif filter_type == "regular":
        move

    if body.dtype != np.uint8:
        # Scale the body to uint8 if mandatory
        cv2.normalize(body, body, 0, 255, cv2.NORM_MINMAX)
        body = body.astype(np.uint8)

Moderne GUI mit CustomTkinter

Jetzt weiß ich nichts über Sie, aber die aktuelle Benutzeroberfläche sieht nicht sehr aus fashionable mir. Versteh mich nicht falsch, es gibt etwas Schönheit im Stil der Oberfläche, aber ich bevorzuge sauberere, modernere Designs. Außerdem sind wir bereits an der Grenze dessen, was Opencv Angebote in Bezug auf UI -Elemente. Ja, keine Schaltflächen, Textfelder, Dropdowns, Kontrollkästchen oder Optionsfelder und keine benutzerdefinierten Layouts. Lassen Sie uns additionally sehen, wie wir das Aussehen und die Benutzererfahrung dieser grundlegenden Anwendung in eine frische und saubere verwandeln können.

Um loszulegen, müssen wir zunächst eine Klasse für unsere App erstellen. Wir erstellen zwei Frames: Der erste enthält unsere Filterauswahl auf der linken Seite und die zweite wickelt die Bildanzeige. Beginnen wir vorerst mit einem einfachen Platzhaltertext. Leider gibt es direkt von CustomTkinter direkt aus der Field OpenCV -Komponente. Daher müssen wir in den nächsten Schritten schnell unsere eigenen erstellen. Aber lassen Sie uns zuerst das grundlegende UI -Format beenden.

import customtkinter


class App(customtkinter.CTk):
    def __init__(self) -> None:
        tremendous().__init__()

        self.title("Webcam Stream")
        self.geometry("800x600")

        self.filter_var = customtkinter.IntVar(worth=0)

        # Body for filters
        self.filters_frame = customtkinter.CTkFrame(self)
        self.filters_frame.pack(facet="left", fill="each", broaden=False, padx=10, pady=10)

        # Body for picture show
        self.image_frame = customtkinter.CTkFrame(self)
        self.image_frame.pack(facet="proper", fill="each", broaden=True, padx=10, pady=10)

        self.image_display = customtkinter.CTkLabel(self.image_frame, textual content="Loading...")
        self.image_display.pack(fill="each", broaden=True, padx=10, pady=10)

app = App()
app.mainloop()

FILTER -Optionsknöpfe

Nachdem das Skelett gebaut ist, können wir unsere Komponenten ausfüllen. Für die linke Seite werde ich dieselbe Liste von verwenden filter_types Um eine Gruppe von Optionsfeldern zu füllen, um den Filter auszuwählen.

        # Create radio buttons for every filter sort
        self.filter_var = customtkinter.IntVar(worth=0)
        for filter_id, filter_type in enumerate(filter_types):
            rb_filter = customtkinter.CTkRadioButton(
                self.filters_frame,
                textual content=filter_type.capitalize(),
                variable=self.filter_var,
                worth=filter_id,
            )
            rb_filter.pack(padx=10, pady=10)

            if filter_id == 0:
                rb_filter.choose()

Bildanzeigekomponente

Jetzt können wir mit dem interessanten Teil beginnen, wie wir unsere OpenCV -Frames in der Bildkomponente angezeigt haben. Weil es keine integrierte Komponente gibt, erstellen wir unsere eigene basierend auf dem CTKLabel. Auf diese Weise können wir einen Ladetext anzeigen, während der Webcam -Stream startet.

...

class CTkImageDisplay(customtkinter.CTkLabel):
    """
    A reusable ctk widget widget to show opencv pictures.
    """

    def __init__(
        self,
        grasp: Any,
    ) -> None:
        self._textvariable = customtkinter.StringVar(grasp, "Loading...")
        tremendous().__init__(
            grasp,
            textvariable=self._textvariable,
            picture=None,
        )

...

class App(customtkinter.CTk):
    def __init__(self) -> None:
        ...

        self.image_display = CTkImageDisplay(self.image_frame)
        self.image_display.pack(fill="each", broaden=True, padx=10, pady=10) 

Bisher hat sich nichts geändert. Wir haben das vorhandene Etikett mit unserer benutzerdefinierten Klassenimplementierung einfach ausgetauscht. In unserem CTKImageDisplay Klasse können wir eine Funktion definieren, um ein Bild in der Komponente anzuzeigen, nennen wir es set_frame.

import cv2
import numpy.typing as npt
from PIL import Picture

class CTkImageDisplay(customtkinter.CTkLabel):
    ...

    def set_frame(self, body: npt.NDArray) -> None:
        """
        Set the body to be displayed within the widget.

        Args:
            body: The brand new body to show, in opencv format (BGR).
        """
        target_width, target_height = body.form(1), body.form(0)

        # Convert the body to PIL Picture format
        frame_rgb = cv2.cvtColor(body, cv2.COLOR_BGR2RGB)
        frame_pil = Picture.fromarray(frame_rgb, "RGB")

        ctk_image = customtkinter.CTkImage(
            light_image=frame_pil,
            dark_image=frame_pil,
            dimension=(target_width, target_height),
        )
        self.configure(picture=ctk_image, textual content="")
        self._textvariable.set("")

Lassen Sie uns das verdauen. Zunächst müssen wir wissen, wie groß unsere Bildkomponente sein wird. Wir können diese Informationen aus der Formeigenschaft unseres Bildarrays extrahieren. Zum Anzeigen des Bildes in tkinterWir brauchen ein Kissen Picture Typ, wir können das OpenCV -Array nicht direkt verwenden. Um ein OpenCV -Array in Kissen umzuwandeln, müssen wir zunächst den Farbraum umwandeln BGR Zu RGB Und dann können wir die verwenden Picture.fromarray Funktion zum Erstellen des Kissenbildobjekts. Als nächstes können wir ein CTKImage erstellen, in dem wir das gleiche Bild verwenden, unabhängig von dem Thema und die Größe nach unserem Rahmen festlegen. Schließlich können wir die Konfigurationsmethode verwenden, um das Bild in unserem Body einzustellen. Am Ende setzen wir auch die Textvariable zurück, um die zu entfernen „Laden…“ Textual content, obwohl er theoretisch hinter dem Bild versteckt sein würde.

Um dies schnell zu testen, können wir das erste Bild unserer Webcam im Konstruktor festlegen. (Wir werden in einer Sekunde sehen, warum dies keine so gute Idee ist)

class App(customtkinter.CTk):
    def __init__(self) -> None:
        ...
        
        cap = cv2.VideoCapture(0)
        _, frame0 = cap.learn()
        self.image_display.set_frame(frame0)

Wenn Sie dies ausführen, werden Sie feststellen, dass das Fenster etwas länger dauert, bis Sie angezeigt werden. Nach einer kurzen Verzögerung sollten Sie jedoch ein statisches Bild von Ihrer Webcam sehen.

NOTIZ: Wenn Sie keine Webcam bereit haben, können Sie auch eine lokale Videodatei verwenden, indem Sie den Dateipfad an die übertragen cv2.VideoCapture Konstruktoraufruf.

Das ist nicht sehr aufregend, da der Body noch nicht aktualisiert wird. Mal sehen, was passiert, wenn wir versuchen, dies naiv zu tun.

class App(customtkinter.CTk):
    def __init__(self) -> None:
        ...

        cap = cv2.VideoCapture(0)
        whereas True:
            ret, body = cap.learn()
            if not ret:
                break

            self.image_display.set_frame(body)

Quick das gleiche wie zuvor, außer jetzt führen wir die Body -Schleife aus wie im vorherigen Kapitel mit der OpenCV -GUI. Wenn Sie dies ausführen, werden Sie sehen … genau nichts. Das Fenster wird nie angezeigt, da wir im Konstruktor der App eine unendliche Schleife erstellen! Dies ist auch der Grund, warum das Programm nach einer Verzögerung im vorherigen Beispiel erst aufgetaucht ist. Die Öffnung des Webcam -Streams ist ein Blockierungsvorgang, und die Ereignisschleife für das Fenster kann nicht ausgeführt werden, sodass es noch nicht angezeigt wird.

Lassen Sie uns dies additionally beheben, indem wir eine etwas bessere Implementierung hinzufügen, mit der die GUI -Ereignisschleife ausgeführt werden kann, während wir den Body ab und zu auch aktualisieren. Wir können die verwenden after Methode von tkinter Planen Sie einen Funktionsaufruf, während Sie den Prozess während der Wartezeit ergeben.


        ...

        self.cap = cv2.VideoCapture(0)
        self.after(10, self.update_frame)

    def update_frame(self) -> None:
        """
        Replace the displayed body.
        """
        
        ret, body = self.cap.learn()
        if not ret:
            return
        
        self.image_display.set_frame(body)

        self.after(10, self.update_frame)

Jetzt haben wir den Webcam -Stream noch im Konstruktor eingerichtet, additionally haben wir dieses Downside noch nicht gelöst. Aber zumindest können wir einen kontinuierlichen Strom von Frames in unserer Bildkomponente sehen.

Filter anwenden

Jetzt, da die Rahmenschleife ausgeführt wird. Wir können unsere Filter von Anfang an erneut implementieren und sie auf unseren Webcam-Stream anwenden. In der Funktion update_frame können wir die aktuelle Filtervariable überprüfen und die entsprechende Filterfunktion anwenden.

    def update_frame(self) -> None:
        ...
        
        # Get the chosen filter sort
        filter_id = self.filter_var.get()
        filter_type = filter_types(filter_id)

        if filter_type == "grayscale":
            body = cv2.cvtColor(body, cv2.COLOR_BGR2GRAY)
        elif filter_type == "blur":
            body = cv2.GaussianBlur(body, ksize=(15, 15), sigmaX=0)
        elif filter_type == "threshold":
            grey = cv2.cvtColor(body, cv2.COLOR_BGR2GRAY)
            _, body = cv2.threshold(grey, thresh=127, maxval=255, sort=cv2.THRESH_BINARY)
        elif filter_type == "canny":
            body = cv2.Canny(body, threshold1=100, threshold2=200)
        elif filter_type == "sobel":
            body = cv2.Sobel(body, ddepth=cv2.CV_64F, dx=1, dy=0, ksize=5)
        elif filter_type == "laplacian":
            body = cv2.Laplacian(body, ddepth=cv2.CV_64F)
        elif filter_type == "regular":
            move

        if body.dtype != np.uint8:
            # Scale the body to uint8 if mandatory
            cv2.normalize(body, body, 0, 255, cv2.NORM_MINMAX)
            body = body.astype(np.uint8)
        if len(body.form) == 2:  # Convert grayscale to BGR
            body = cv2.cvtColor(body, cv2.COLOR_GRAY2BGR)
        
        self.image_display.set_frame(body)

        self.after(10, self.update_frame)

Und jetzt sind wir wieder zur vollen Funktionalität der Anwendung, Sie können jeden Filter auf der linken Seite auswählen und er wird in Echtzeit auf den Webcam-Feed angewendet!

Multithreading und Synchronisation

Obwohl die Anwendung so ausgeführt wird, gibt es einige Probleme mit der aktuellen Artwork und Weise, wie wir unsere Body -Schleife ausführen. Derzeit läuft alles in einem einzigen Thread, dem Haupt -GUI -Thread. Aus diesem Grund sehen wir am Anfang nicht sofort, dass das Fenster auftaucht, und unsere Webcam -Initialisierung blockiert den Haupt -Thread. Stellen Sie sich jetzt vor, wenn wir eine schwerere Bildverarbeitung durchführen und die Bilder möglicherweise über neuronales Netzwerk ausgeführt haben, möchten Sie nicht, dass Ihre Benutzeroberfläche immer blockiert wird, während das Netzwerk inferenziert. Dies führt zu einer sehr nicht reagierenden Benutzererfahrung beim Klicken auf die UI -Elemente!

Eine bessere Möglichkeit, dies in unserer Anwendung zu behandeln Trennen Sie die Bildverarbeitung von der Benutzeroberfläche. Im Allgemeinen ist dies quick immer eine gute Idee, Ihre GUI-Logik von jeglicher Artwork von nicht trivialer Verarbeitung zu trennen. In unserem Fall werden wir additionally einen separaten Thread ausführen, der für die Bildschleife verantwortlich ist. Es wird die Frames aus dem Webcam -Stream gelesen und die Filter angewendet.

NOTIZ: Python -Threads sind nicht „actual“ Themen in gewisser Weise, dass sie nicht die Fähigkeit haben, auf verschiedenen logischen CPU -Kernen zu laufen, und daher nicht Wirklich parallel laufen. In Python Multithreading wechselt der Kontext zwischen den Threads, aber aufgrund des GIL kann ein einzelner Python -Prozess nur einen physischen Faden ausführen. Falls Sie es wollen „actual“ Parallele Verarbeitung müssen Sie verwenden Multiprozessierung. Da unser Prozess hier nicht CPU -gebunden ist, sondern tatsächlich E/O gebundenMultithreading reicht aus.

class App(customtkinter.CTk):
    def __init__(self) -> None:
        ...

        self.webcam_thread = threading.Thread(goal=self.run_webcam_loop, daemon=True)
        self.webcam_thread.begin()

    def run_webcam_loop(self) -> None:
        """
        Run the webcam loop in a separate thread.
        """
        self.cap = cv2.VideoCapture(0)
        if not self.cap.isOpened():
            return

        whereas True:
            ret, body = self.cap.learn()
            if not ret:
                break

            # Filters
            ...

            self.image_display.set_frame(body)

Wenn Sie dies ausführen, werden Sie jetzt sehen, dass sich unser Fenster sofort öffnet und wir sogar unseren Ladetext sehen, während sich der Webcam -Stream öffnet. Sobald der Strom beginnt, flackern die Rahmen. Abhängig von vielen Faktoren können Sie zu diesem Zeitpunkt unterschiedliche visuelle Artefakte oder Fehler aufweisen.

WARNUNG: Picture blinkend

Warum passiert das? Das Downside ist, dass wir gleichzeitig versuchen, den neuen Body zu aktualisieren, während die interne Aktualisierungsschleife der Benutzeroberfläche möglicherweise die Informationen des Arrays verwendet, um ihn auf dem Bildschirm zu zeichnen. Sie konkurrieren beide um das gleiche Body -Array.

Es ist im Allgemeinen keine gute Idee, die UI -Elemente direkt aus einem anderen Thread zu aktualisieren. In einigen Frameworks kann dies sogar verhindert werden, und die Ausnahmen erhöhen. In TkinterWir können es tun, aber wir werden seltsame Ergebnisse erzielen. Wir brauchen irgendeine Artwork von Synchronisation zwischen unseren Fäden. Dort ist das Queue kommt ins Spiel.

Sie sind wahrscheinlich mit Warteschlangen aus dem Lebensmittelgeschäft oder den Themenparks vertraut. Das Konzept der Warteschlange hier ist sehr ähnlich: Das erste Component, das in die Warteschlange eingehtFIrst ICHN FIrst Out).

In diesem Fall wollen wir eigentlich nur eine Warteschlange mit einem einzelnen Component, einer einzelnen Slot -Warteschlange. Die Warteschlangenimplementierung in Python ist Thread-Securewas bedeutet, dass wir können setzen Und erhalten Objekte aus der Warteschlange aus verschiedenen Threads. Perfekt für unseren Anwendungsfall. In dem Verarbeitungs -Thread wird die Bildarrays in die Warteschlange gebracht, und der GUI -Thread versucht, ein Component zu erhalten, aber nicht blockieren, wenn die Warteschlange leer ist.

class App(customtkinter.CTk):
    def __init__(self) -> None:
        ...

        self.queue = queue.Queue(maxsize=1)

        self.webcam_thread = threading.Thread(goal=self.run_webcam_loop, daemon=True)
        self.webcam_thread.begin()

        self.frame_loop_dt_ms = 16  # ~60 FPS
        self.after(self.frame_loop_dt_ms, self._update_frame)
    
    def _update_frame(self) -> None:
        """
        Replace the body within the picture show widget.
        """
        attempt:
            body = self.queue.get_nowait()
            self.image_display.set_frame(body)
        besides queue.Empty:
            move

        self.after(self.frame_loop_dt_ms, self._update_frame)

    def run_webcam_loop(self) -> None:
        ...

        whereas True:
            ...

            self.queue.put(body)

Beachten Sie, wie wir den direkten Anruf an die verschieben set_frame Funktion aus der Webcam -Schleife, die in seinem eigenen Thread zu dem ausgeführt wird _update_frame Funktion, die im Haupt -Thread ausgeführt wird, wiederholt geplant in 16 ms Intervalle.

Hier ist es wichtig, die zu verwenden get_nowait Funktion im Haupt -Thread, sonst würden wir dort blockieren, wenn wir die GET -Funktion verwenden würden. Dieser Anruf tut nicht blockierenaber erhöht a queue.Empty Ausnahme, wenn es kein Component gibt, das sie abholen können, müssen wir dies fangen und es ignorieren. In der Webcam -Schleife können wir die Blockierungsfunktion verwenden, da es keine Rolle spielt, dass wir Block Die run_webcam_loopEs gibt nichts anderes, das dort rennen muss.

Und jetzt läuft alles wie erwartet, keine blinkenden Frames mehr!

Abschluss

Kombinieren eines UI -Frameworks wie Tkinter mit Opencv Ermöglicht es uns, moderne Anwendungen mit einer interaktiven grafischen Benutzeroberfläche zu erstellen. Aufgrund der im Haupt-Thread ausgeführten Benutzeroberfläche führen wir die Bildverarbeitung in einem separaten Thread aus und synchronisieren die Daten zwischen den Threads mithilfe einer einzelnen Slot-Warteschlange. Im folgenden Repository finden Sie eine gereinigte Model dieser Demo mit einer modulareren Struktur. Lassen Sie mich wissen, wenn Sie mit diesem Ansatz etwas Interessantes aufbauen. Aufpassen!



Schauen Sie den vollständigen Quellcode im Github Repo an:

https://github.com/trflorian/ctk-opencv


Von admin

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert