Threads mit QtConcurrent



  • @makopo sagte in Threads mit QtConcurrent:

    Ich habe gerade herausgefunden das m_futuere.waitForFinished() das öffnen weiterer Threads verhindert.

    Wo rufst du das auf? Zeig etwas mehr Kontext. Kommt mir noch nicht so optimal vor.



  • @Schlangenmensch

    Oh stimmt, das habe ich nicht bedacht. Der Slot (und damit ReceiveFrame) wird ja jedes mal aufgerufen. Aber dort kann ich am Einfachsten die Frames abgreifen.

    Aber ja, das ist nicht optimal.
    waitForFinish() rufe ich mit der Instanz m_future auf.

    void VideoScreen::ReceiveFrame(CComPtr<IDeckLinkVideoFrame> theFrame) {
    	if (m_previewHelper != nullptr) {
    		m_previewHelper->SetFrame(theFrame);
    
    		ConvertVideoFormat cvf;
    		m_future = QtConcurrent::run(&cvf, &ConvertVideoFormat::DeckLinkToCv, theFrame);
                    m_future.waitForFinished();
    
    		QWidget::update();
    	}
    }
    


  • @makopo So bringt dir der neue Thread nix! Du startest den neuen Thread und wartest dann direkt auf die Beendigung. So kannst du das direkt im Hauptthread machen.



  • @Schlangenmensch

    Ok, danke. Tatsächlich fängt der Stream auch an zu ruckeln wenn ich waitForFinished aufrufe. Ohne Aufruf ist das nicht so und der Stream läuft einigermaßen flüssig.

    ReceiveFrame scheint dann ja nicht der beste Ort zu sein, um die empfangenen Frames an die Konvertierungsfunktion zu übergeben. Allerdings schaffe ich es auch nicht IDeckLinkVideoFrame* theFrame als Parameter an einer anderen Stelle zu übergeben, da theFrame sonst in jedem Fall immer 0 ist.

    Ich hatte es per Signal/Slot probiert, aber auch hier wird die Konvertierungsfunktion nicht aufgerufen.

    GraphicScreen::GraphicScreen(QWidget* parent) :
        QWidget(parent)
    {
        m_videoScreenHelper = new VideoScreenHelper(parent);
        QObject::connect(m_videoScreenHelper, &VideoScreenHelper::SendFrame, this, &GraphicScreen::DeckLinkToCv, Qt::QueuedConnection);
    }
    

    SendFrame sendet dabei IDeckLinkVideoFrame* theFrame an den Slot. Kann man das so umsetzten oder gibt es bessere Lösungen? Stehe da gerade echt auf dem Schlauch...

    EDIT: Sende IDeckLinkVideoFrame* theFrame nun an den Slot zur Konvertierung. Jetzt muss die Konvertierung meiner Meinung nach in einen anderen Thread ausgelagert werden?



  • DeckLinkToCv bekommt einen Zeiger als Parameter, das passt nicht. Das Frame muss am Leben erhalten werden. Also evtl. den CComPtr als Kopie übergeben.
    Ich würde spontan eher zu einem dedizierten Thread tendieren. Evtl. ein Thread, der einliest, konvertiert, und die Frames in einer Queue ablegt. Und der Hauptthread stellt sie dar oder wie auch immer.

    Kann es sein, dass du einen ziemlich langsamen Rechner hast? Wundert mich, dass das nicht in Echtzeit geht.



  • @Mechanics

    Ja, der Rechner ist ein wenig langsamer. Aber selbst wenn er das nicht wäre, würde es doch Sinn machen die Formatwandlung zwecks Perfomance in einem anderen Thread durchzuführen?
    Ich meine 25 Bilder die Sekunde nach cv::Mat konvertieren und dann auch noch die Pixelwerte auslesen lassen, dürfte doch für eine ganz gute Auslastung sorgen?



  • @makopo sagte in Threads mit QtConcurrent:

    Ich meine 25 Bilder die Sekunde nach cv::Mat konvertieren und dann auch noch die Pixelwerte auslesen lassen, dürfte doch für eine ganz gute Auslastung sorgen?

    Glaub ich eher nicht... Ich kann jetzt grad nichts mit Sicherheit behaupten. Ist schon paar Jahre her, dass ich was ähnliches gemacht habe, und ich kann mich auch nicht erinnern, was das jetzt ganz genau war und wie schnell das war. Aber so wie ich mich dran erinnere, war da auch Konvertierung nach OpenCv, dann ein bisschen Verarbeitung und paar einfache Effekte, und Ausgabe. Und das ging in Echtzeit.
    Das Einlesen/Konvertieren hatte ich aber über ffmpeg gemacht, mit Hardware-Unterstützung.



  • @Mechanics

    Okay, bin für jede Mini-Erfahrung dankbar...
    Also wenn ich die Pixelwerte für RGB von einer 300 x 200 Pixelgrafik auslesen lassen dauert das über 5 Sekunden bis jeder Pixel auf der Konsole ausgegeben wurde. Für einen 1080p25 Videostream ist das nicht ganz so cool.
    Schreit nach einem neuen Rechner, wobei ich meine mal gelesen zu haben das das auch am Compiler liegen könnte.



  • @makopo sagte in Threads mit QtConcurrent:

    bis jeder Pixel auf der Konsole ausgegeben wurde.

    Das hört sich doch eher nach dem Flaschenhals an. Was machst du da genau?



  • @makopo sagte in Threads mit QtConcurrent:

    @Mechanics

    Okay, bin für jede Mini-Erfahrung dankbar...
    Also wenn ich die Pixelwerte für RGB von einer 300 x 200 Pixelgrafik auslesen lassen dauert das über 5 Sekunden bis jeder Pixel auf der Konsole ausgegeben wurde. Für einen 1080p25 Videostream ist das nicht ganz so cool.
    Schreit nach einem neuen Rechner, wobei ich meine mal gelesen zu haben das das auch am Compiler liegen könnte.

    Das hört sich komisch an: Ich habe mal ein bisschen Bildverstehen auf 'ner 800 Mhz CPU gemacht, die in so 'n Fußballroboter eingebaut war. Da musste man ein bisschen aufpassen und hat z.B. nicht jeden Pixel verarbeitet, weil man auch noch andere Sachen machen musste, Aber 300 * 200 * 3 Werte auslesen und ausgeben über 5 Sekunden... Ist das die Ausgabe auf der Konsole? Das dauert schon mal. Wie lange dauert es, wenn du die alle, sagen wir, aufaddierst und nur einen Wert ausgibst? (Ich will die Ausgabe minimieren 😉 )

    Was für eine CPU hast du denn? Welchen Compiler benutzt du? Und, mit welchen Flags kompilierst du? Wie sieht der Code aus, mit dem du deine Messung gemacht hast?



  • @Mechanics sagte in Threads mit QtConcurrent:

    Das hört sich doch eher nach dem Flaschenhals an. Was machst du da genau?

    void GraphicScreen::ReadPixelData() {
    
        unsigned char r, g, b;
        cv::Mat frame = cv::imread("C://ImageTest/test.jpg");
    
        for (int x = 0; x < frame.rows; ++x)
        {
            cv::Vec3b* pixel = frame.ptr<cv::Vec3b>(x);
            for (int y = 0; y < frame.cols; ++y)
            {
                r = pixel[y][2];
                g = pixel[y][1];
                b = pixel[y][0];
    
                qDebug() << "R" << r  << "G" << g << "B" << b;
            }
        }
    }
    

    Das ist die Funktion mit der ich die Pixel auslese. Vielleicht ist die auch nicht besonders effizient. Und parallel läuft in meinem Programm kein anderer Prozess (also auch nicht das einlesen des Videostreams)

    @Schlangenmensch sagte in Threads mit QtConcurrent:

    Was für eine CPU hast du denn? Welchen Compiler benutzt du? Und, mit welchen Flags kompilierst du?
    CPU: i3 mit 3 GHz (ist mir ein wenig peinlich, aber der Rechner wurde mir gestellt, habe vorher ehrlich gesagt auch nicht darauf geachtet)
    Compiler: MSVC2019 C++17
    Flags: Bewusst habe ich keine Flags gesetzt. Ich habe über Qt ein makefile erstellt und in VS die Libraries eingebunden. Das war mein Setup. Laufen lassen tue ich das Programm im Debug Mode.

    Am Anfang des Projekts dachte ich ja auch das sowas theoretisch in Echtzeit funktionieren müsste. Aber dann setzt man die ersten Sachen um und merkt das es doch nicht so funktioniert wie geplant und kommt ins Zweifeln und in Erklärungsnot ^^.



  • @makopo sagte in Threads mit QtConcurrent:

    Das ist die Funktion mit der ich die Pixel auslese. Vielleicht ist die auch nicht besonders effizient.

    Lass mal das qDebug zum Test weg. Ich vermute, das braucht sehr viel mehr Zeit, als das Auslesen.



  • @makopo sagte in Threads mit QtConcurrent:

    Was für eine CPU hast du denn? Welchen Compiler benutzt du? Und, mit welchen Flags kompilierst du?

    CPU: i3 mit 3 GHz (ist mir ein wenig peinlich, aber der Rechner wurde mir gestellt, habe vorher ehrlich gesagt auch nicht darauf geachtet)
    Compiler: MSVC2019 C++17
    Flags: Bewusst habe ich keine Flags gesetzt. Ich habe über Qt ein makefile erstellt und in VS die Libraries eingebunden. Das war mein Setup. Laufen lassen tue ich das Programm im Debug Mode.

    Die CPU sollte für das bisschen ausreichen.
    Visual Studio setzt von sich aus ganz viele verschiedene Compilerflags, je nach verwendeter Konfiguration.

    Nachdem du QDebug, wie von @Mechanics vorgeschlagen, rausgenommen hast, kannst du die Konfiguration mal auf Release umstellen, kompilieren und ausführen. Das führt dazu, dass Visual Studio die Optimierungsflags setzt und ich meine auch, per Default Debugsymbole ausschaltet.

    Ich habe jetzt nicht in die Doku von qDebug geschaut, aber ich würde vermuten, dass das in der Release Konfiguration von QT aus, schon abgeschaltet wird.

    Dann ist noch interessant, wie OpenCV und QT kompiliert wurden. Da sollte man dann nämlich auch gegen die "Release" Libs linken.

    Generell gilt: Performance im Debug Build zählt nicht 😉



  • @Schlangenmensch sagte in Threads mit QtConcurrent:

    Ich habe jetzt nicht in die Doku von qDebug geschaut, aber ich würde vermuten, dass das in der Release Konfiguration von QT aus, schon abgeschaltet wird.

    Das sollte auch im Release Build funktionieren.



  • Am Anfang des Projekts dachte ich ja auch das sowas theoretisch in Echtzeit funktionieren müsste

    zwei schleifen übern memory um Bildpunkte einzeln auslesen .... glaub nicht.
    OpenCV kann Formatkonvertierungen in Echtzeit auch nur mit "Tricks" (simd / multimedia Befehlssätze (AVX) / GPU)

    Darf ich fragen für was die RGBWerte auslesen willst/musst? "Normal" macht man, wenn es echtzeit sein muss, alles blockweisse .....

    Aber wenn die Anforderung heisst, lese die Bildpunkte von einem Image einzeln aus ... wie soll das in Echtzeit gehen?

    Das sind bei dir schon mal 300 * 200 * 3 * 30 ? Aufrufe in der sekunde (mit einzelnem Farbkanal) .... wenn nur einen Aufruf pro pixel hasst ... und den rest wegoptimieren könntest.



  • @RHBaum sagte in Threads mit QtConcurrent:

    Das sind bei dir schon mal 300 * 200 * 3 * 30 ? Aufrufe in der sekunde (mit einzelnem Farbkanal)

    Ja und? Das sind nur 5.4 Mio Aufrufe. Bei mehreren Ghz Takt und 10-30 GB/s Ram Bandbreite.



  • Ich denke mal das das Problem war das qDebug() zeilenweise in der Schleife aufgerufen wurde. Stattdessen habe ich versucht, die Pixelwerte für jeden Kanal jeweils in einem Vector zu speichern. Diese Funktion rufe ich dann in der Funktion DrawGraphauf, die mir aus den Werten einen Graph zeichnen soll.
    Draw Graph wird dann über das Anklicken eines Button aufgerufen und der Graph wird sofort gezeichnet.
    Mit einem einzelnen Bild funktioniert es sehr schnell. Wie es mit 25 Bildern die Sekunde im HD Format aussieht kann ich noch nicht sagen, da ich mit der Formatkonvertierung noch nicht weitergegekommen bin.

    void GraphicScreen::ReadPixelData() {
        uint redPx, greenPx, bluePx;
        uint redSum, greenSum, blueSum;
    
        //hier wird ein in cv::Mat RGB konvertiertes Format übergeben
        cv::Mat frame = cv::imread("C://ImageTest/test.jpg");
    
        if (frame.empty())
            qDebug() << "No image found in ReadPixelData().";
    
        for (int r = 0; r < frame.rows; ++r) {
            cv::Vec3b* pixel = frame.ptr<cv::Vec3b>(r); //Vec3b defines a vector with 3 byte entries
    
            for (int c = 0; c < frame.cols; ++c) {
                redPx = pixel[c][2]; 
                greenPx = pixel[c][1];
                bluePx = pixel[c][0];
    
                //push single channel pixel into a vector
                m_redValues.push_back(redPx);
                m_greenValues.push_back(greenPx);
                m_blueValues.push_back(bluePx);
            }
        }
    }
    
    cv::Mat GraphicScreen::DrawGraph(int w, int h) {
        ReadPixelData();
        cv::Mat data(m_redValues);
        qDebug() << "Red:" << m_redValues;
        cv::Ptr<cv::plot::Plot2d> plot = cv::plot::Plot2d::create(data);
        cv::Mat image;
        plot->setPlotBackgroundColor(cv::Scalar(30, 30, 30));
        plot->setPlotAxisColor(cv::Scalar(30, 30, 30));
        plot->setPlotGridColor(cv::Scalar(30, 30, 30));
        plot->setPlotLineColor(cv::Scalar(0, 0, 255));
        plot->setShowText(false);
        plot->render(image);
        return image;
    }
    

    So etwas in der Art programmiere ich übrigens (nur ohne Manipulationsmöglichkeit) : Link



    1. ich hoffe du hasst die vectoren vorher reserved 🙂

    2. Du bekommst als Ergebniss deiner ReadPixelData funktion genau was ?
      So wie ich es sehe das bild in die RGB Komponenten gesplittet .... dein m_redValues, m_greenValues, m_blueValues
      (schau dir mal cv::split an)

    was ich noch ned ganz verstehe ist cv::plot::Plot2d::create(data);
    data ist in deinem Fall der RotKanal vom Bild ....

    wenn den so plotten willst, wird deine X zu column + row * column vom bild, und Y wird der Farbwert da ....
    Glaub nicht das es das ist was du haben willst 🙂



  • Hi,

    @RHBaum sagte in Threads mit QtConcurrent:

    ich hoffe du hasst die vectoren vorher reserved

    Das habe ich noch nicht gemacht. Würde ich aber einbauen wenn das ganze Gerüst erstmal steht.

    @RHBaum sagte in Threads mit QtConcurrent:

    Du bekommst als Ergebniss deiner ReadPixelData funktion genau was ?

    Über ReadPixel greife ich auf einzelnen Vektoren zu. m_redValues speichert ja die Pixelwerte für Rot und den Vektor übergebe ich dann an die Plot2D Funktion. Ich habe aber schon gemerkt das Plot2D ungeeignet ist, da man damit nicht drei Graphen in einem Koordinatensystem zeichnen kann.
    Probiere das Extrahieren nun mit der cv::split und das zeichnen mit cv:line.



  • @makopo sagte in Threads mit QtConcurrent:

    Das habe ich noch nicht gemacht.

    Ich hoffe mit der Zeit wird das auch bei Dir zum reflex 🙂 Vector wird groß, du weisst die größe -> reserven

    Probiere das Extrahieren nun mit der cv::split

    Der Unterschied zu deiner Methode: cv::split liefert eine Matrix2D statt einem reinem array (vector)
    da ist der unterschied, die matrix kennt ihre X dimensionen, der vector kennt keine (hat nur 1) ... Aber die Daten selber sollten gleich sein.
    Vorteil der Matrix , du kannst SIe in OpenCV direkt anzeigen, dann siehst dein Bild als Farbkanal ....

    Bin nicht sicher ob die cv Matrix2D einen linearen zugriff hat (hab lange nix mehr mit openCV gemacht), wenn nicht muesstes deinen Index in zeile,spalte umrechenen, sollte kein Problem sein, da die Dimensionen ja kennst (damit wie aufn vector zugreifen kannst).

    Ich verstehe immer noch nicht, was du fürn diagram zeichnen willst ....
    Die y Achse soll dein red farbwert sein ... (und grü und blau) aber was soll die X Achse sein ....

    Wenn das einm klassisches Farb Histogramm werden soll, dann ist deine X Achse der Helligkeitswert (von 0 - 1) und nix was von der Pixelpostion (row/column) abhängig ist
    Da wirst noch bissi rechnen dürfen ....

    Ciao ...


Anmelden zum Antworten