Seite 1 von 1

OpenCV Frame Farbanteile (channels)

Verfasst: Dienstag 28. Mai 2019, 19:49
von Felix92
Huhu,
gibt es eine Möglichkeit aus einem Video jeden Frame auszulesen ob er grüner, blauer oder roter ist ?
Also quasi wenn Frame mehr grüne Anteile als blaue oder rote hat dann do ...
Meine Idee:
green =100 * channel[1] / (channel[0] + channel[1] + channel[2])
if green > 50:
do...
Oder gibt es da vlt. sogar schon eine Methode welche mir den prozentualen Anteil (RGB) aus einem Frame zurück gibt ?
In der docu bin ich leider nicht fündig geworden.
MfG Felix

Re: OpenCV Frame Farbanteile (channels)

Verfasst: Dienstag 28. Mai 2019, 20:30
von __deets__
Das klingt nach einem x/y-Problem. Was willst du eigentlich erreichen?

Re: OpenCV Frame Farbanteile (channels)

Verfasst: Dienstag 28. Mai 2019, 21:00
von Felix92
Naja ich hab ein Video wo manchmal auf die "Schultafel" gezoomt wird dementsprechend höherer grün Anteil wenn das der Fall ist soll halt etwas anderes passieren als wenn nicht auf die Tafel gezoomt wird bzw wenn Tafel wird mit openshot ein Clip über die Zeit wo er an der Tafel ist erstellt und wenn das nicht der Fall ist passiert nichts. Dient quasi dazu um ein Video zu analysieren.

Re: OpenCV Frame Farbanteile (channels)

Verfasst: Mittwoch 29. Mai 2019, 06:12
von ThomasL
wie wäre es mit der Berechnung und Vergleich des mean() über den jeweiligen RGB-Channel?

Re: OpenCV Frame Farbanteile (channels)

Verfasst: Mittwoch 29. Mai 2019, 08:34
von Felix92
Danke ThomasL das sollte funktionieren :)

Re: OpenCV Frame Farbanteile (channels)

Verfasst: Mittwoch 29. Mai 2019, 08:47
von __deets__
Ich würde das so nicht machen. Für Betrachtungen von Farben würde ich in HSV wechseln, und dann mit der passenden Farbe in einem gewissen Bereich filtern. Der entstehende Blob wird dann per kontour extraction ausgemessen in seiner Größe.

Arbeiten auf den RGB-Werten zur Farbbeurteilung ist nahezu nie erfolgreich, weil der Farbraum schlecht Distanzen zu anderen Farben berechenbar macht.

Re: OpenCV Frame Farbanteile (channels)

Verfasst: Mittwoch 29. Mai 2019, 09:23
von Felix92
Huhu deets,
das ganze Problem dabei ist das relativ häufig und umständlich gezoomt wird weshalb es nicht möglich ist einen festen Bereich zu setzen.
(Das hatte ich davor hat auch super geklappt bis ich dann erfahren habe das dort auch gezoomt wird :idea: )
Also so wie ich es jetzt habe klappt es (auch mit zoom) bin natürlich gerne offen für Vorschläge und alternative Ideen. :D

Re: OpenCV Frame Farbanteile (channels)

Verfasst: Mittwoch 29. Mai 2019, 09:34
von __deets__
Wenn’s klappt, schön. Mein Ansatz hat mit zoomen an sich erstmal nix zu tun und ist robuster bei beleuchtungsveränderungen (in gewissem Rahmen), aber wenn du was hast, das tut - benutz es.

Re: OpenCV Frame Farbanteile (channels)

Verfasst: Mittwoch 29. Mai 2019, 19:47
von Felix92
Nun habe ich allerdings gemerkt das nen 90min Video beim "einlesen" echt ewig braucht gibt es dort einen simpleren Weg als das Video in einem Thread "vorzuladen" ?
Nur jeden 2ten Frame oder ähnliches zu nehmen ist leider keine Alternative.

Re: OpenCV Frame Farbanteile (channels)

Verfasst: Mittwoch 29. Mai 2019, 21:56
von __deets__
Nein. Du kannst probieren andere Tools wir ffmpeg du benutzen, wenn der Frame dann ein numpy Array ist, ist OpenCv zufrieden.

Re: OpenCV Frame Farbanteile (channels)

Verfasst: Mittwoch 29. Mai 2019, 22:17
von Felix92
Ok ich habe dazu gerade etwas gefunden allerdings verstehe ich von dem ffmpeg Ausdruck fast nichts :/
import subprocess

c = 'ffmpeg -y -i ' + video_input_path + ' -r 30 -s 112x112 -c:v libx264 -b:v 3M -strict -2 -movflags faststart '+video_output_path

subprocess.call(c, shell=True)

Könnte mir dazu vlt. jemand kurz erklären wie ich diese ffmpeg Ausdrücke zu handhaben habe :D
Letztendlich möchte ich ja nur die fps eines Videos welches ich schon eingelesen habe erhöhen Standard sind 21fps ich würde diese wenn möglich gerne auf 500-1000 fps drücken oder noch höher wenn möglich ohne Verluste.
Danke für eure Antworten :)
MfG Felix

Re: OpenCV Frame Farbanteile (channels)

Verfasst: Donnerstag 30. Mai 2019, 07:26
von Sirius3
@Felix92: Du hast also ein Programm, das wir nicht kennen, das zu lange für die Analyse eines Videos braucht. Dann hast Du entweder ein Problem, das lange braucht, oder einen ungeeigneten Algorithmus benutzt, oder diesen ganz einfach ineffizient programmiert.
Da hilft ein schnellerer Rechner, oder in den zwei letzten Fällen, geschickter Programmieren.

Re: OpenCV Frame Farbanteile (channels)

Verfasst: Donnerstag 30. Mai 2019, 09:15
von __deets__
Es geht um vorspulen. Um beim testen denke ich mal bestimmte stellen anzufahren.

Re: OpenCV Frame Farbanteile (channels)

Verfasst: Freitag 31. Mai 2019, 11:33
von Felix92
Also hier mal wie ich das Video "erstelle":

Code: Alles auswählen

def large_video_cut(self, fps):
        """
        a method to get the part of the speaker from the "main video" and save it in the project folder
        and create a object of this
        """

        video_file = self.video_data
        folder = Path(self.folder_path, self.folder_name)

        cap = cv2.VideoCapture(str(video_file))

        large_video_name = 'board_video.mp4'
        fourcc = cv2.VideoWriter_fourcc(*'mp4v')
        out = cv2.VideoWriter(os.path.join(folder,str(large_video_name)), fourcc , fps, (938, 530))

        if(cap.isOpened() == False):
            print("Error opening video stream or file")

        while(cap.isOpened()):
            ret, frame = cap.read()
            if ret == True:
                frame = frame[275:805, 17:955]
                out.write(frame)

            else:
                break
        cap.release()
        out.release()
        cv2.destroyAllWindows()
        new_large_video_path = Path(folder, large_video_name)
        self.files.append(new_large_video_path)
        return BoardVideo(out)
Und die Analyse:

Code: Alles auswählen

def board_area(self, clip_prefix):
        """
        a method that analyse the video frame per frame and save the Clips (Board) in a list
        """
        video = cv2.VideoCapture(str(self.file_path))
        try:
            times = list()
            clip_numbers = count()

            for frame_number in count():
                is_ok, frame = video.read()

                if not is_ok:
                    break

                average = cv2.mean(frame)
                sum = average[0] + average[1] + average[2]
                percentage_green = (100 * average[1]) / sum

                if percentage_green > 40:
                    times.append(video.get(cv2.CAP_PROP_POS_MSEC) / 1000)
                else:
                    if times:
                        clip = openshot.Clip(
                                '{}{}'.format(clip_prefix, next(clip_numbers))
                            )
                        clip.Start(times[0])
                        clip.End(times[-1])
                        self.subvideos.append(clip)
                        times.clear()

        finally:
            video.release()
            cv2.destroyAllWindows()
In der zweiten Methode würde ich das Video gerne wesentlich schneller "abarbeiten"

Re: OpenCV Frame Farbanteile (channels)

Verfasst: Freitag 31. Mai 2019, 14:01
von __blackjack__
@Felix92: Bezüglich des `ffmpeg`-Aufrufs: Das ``shell=True`` sollte man nicht verwenden, sondern den Befehl und die Argumente als Liste übergeben:

Code: Alles auswählen

    command = [
        'ffmpeg',
        '-y',
        '-i', video_input_path,
        '-r', '30',
        '-s', '112x112',
        '-c:v', 'libx264',
        '-b:v', '3M',
        '-strict', '-2',
        '-movflags', 'faststart',
        video_output_path,
    ]
    subprocess.run(command)
`video_data` ist kein guter Name für einen Dateinamen/Pfad – da erwartet man die Videodaten selbst oder Daten über ein Video. Und das Attribut dann lokal an einen anderen Namen zu binden ist auch nicht schön. Zumal `video_file` auch etwas verwirrend ist, denn um ein Dateiobjekt handelt es sich ja auch nicht.

Der Zieldateiname in `large_video_cut()` – was eher `cut_large_video()` heissen sollte – wird über die Methode verteilt und zwei mal auf unterschiedliche Arten erstellt. Das sollte nur *einmal* passieren und entweder mit `Path` oder mit `os.path.join()`.

Weder ``if`` noch ``while`` sind Funktionen, also sollte man die auch nicht so schreiben als wären es welche. Die Klammern gehören da nicht hin.

Vergleiche mit literalen Wahrheitswerten macht man nicht. Da kommt nur wieder ein Wahrheitswert heraus, also kann man auch gleich den Wert nehmen mit dem man vergleicht oder mit dessen Negation mit ``not``. Bei `isOpened()` machst Du das ja auch schon einmal richtig. Warum diese Inkonsistenz?

Der Test ob `cap` geöffnet werden konnte, sollte eigentlich stattfinden bevor man mit allem weiteren weiter macht und vor allem auch verhindern das weiter gemacht wird, wenn das Öffnen fehlgeschlagen ist. Die `release()`-Aufrufe sollten durch ``try``:``finally`` sichergestellt werden. Und bei `VideoWriter` prüfst Du gar nicht, ob der funktioniert oder nicht.

Der ``return``-Wert sieht unsinnig aus: Ein `BoardVideo`-Objekt das einen geschlossenes `VideoWriter`-Objekt übergeben bekommt? Was soll denn damit noch angefangen werden?

Zwischenstand (ungetestet):

Code: Alles auswählen

    def cut_large_video(self, fps):
        """Get the part of the speaker from the "main video" and save it in the
        project folder and create an object of this.
        """
        capture = cv2.VideoCapture(str(self.video_data))
        try:
            if not capture.isOpened():
                raise RuntimeError('Error opening video stream or file')
            
            new_filename = Path(
                self.folder_path, self.folder_name, 'board_video.mp4'
            )
            fourcc = cv2.VideoWriter_fourcc(*'mp4v')
            writer = cv2.VideoWriter(str(new_filename), fourcc, fps, (938, 530))
            try:
                if not writer.isOpened():
                    raise RuntimeError('Error opening video stream or file')

                while True:
                    is_ok, frame = capture.read()
                    if not is_ok:
                        break
                    writer.write(frame[275:805, 17:955])
            finally:
                writer.release()
            
            self.files.append(new_filename)
            # 
            # TODO Passing a closed `VideoWriter` object looks wrong‽
            # 
            return BoardVideo(writer)
        finally:
            capture.release()
            cv2.destroyAllWindows()
`board_area()` ist kein guter Name für eine Funktion oder Methode. Solche Namen sollten die Tätigkeit beschreiben und `board_area` beschreibt keine Tätigkeit.

Du machst IMHO zu viel kopieren und einfügen. Hier ist jetzt ein `frame_number` was nicht benötigt wird, was nur da ist, weil diese Methode eine Kopie eines bereits vorhandenen Versuchs ist.

`sum` ist der Name einer eingebauten Funktion, den sollte man nicht an etwas anderes binden. Die Funktion kann man hier sogar verwenden!

Auch in dieser Methode wird nicht sinnvoll darauf reagiert wenn die Videodatei nicht geöffnet werden konnte.

Zwischenstand:

Code: Alles auswählen

    # 
    # TODO Come up with a better method name.
    # 
    def board_area(self, clip_prefix):
        """Analyse the video frame by frame and save the Clips (Board) in a
        list.
        """
        video = cv2.VideoCapture(str(self.file_path))
        try:
            if not video.isOpened():
                raise RuntimeError('Error opening video stream or file')
            
            times = list()
            clip_numbers = count()

            while True:
                is_ok, frame = video.read()
                if not is_ok:
                    break

                average = cv2.mean(frame)
                percentage_green = 100 * average[1] / sum(average)
                if percentage_green > 40:
                    times.append(video.get(cv2.CAP_PROP_POS_MSEC) / 1000)
                else:
                    if times:
                        clip = openshot.Clip(
                            '{}{}'.format(clip_prefix, next(clip_numbers))
                        )
                        clip.Start(times[0])
                        clip.End(times[-1])
                        self.subvideos.append(clip)
                        times.clear()
        finally:
            video.release()
            cv2.destroyAllWindows()
Ich sehe nicht wo diese Methode Zeit vertrödelt. Wenn die zu langsam ist, dann ist das halt so langsam. Sollte es tatsächlich an Python liegen, müsstest Du es in C++ umschreiben. Sowohl OpenCV als auch OpenShot haben da ja eine entsprechende API.

Re: OpenCV Frame Farbanteile (channels)

Verfasst: Sonntag 2. Juni 2019, 21:33
von Felix92
Huhu vielen Dank für deine Antwort :)
Das Problem war das die Daten nicht weiter verwendet wurden bis dato und deshalb alles so ewig gebraucht hat (Arbeitsspeicher) jetzt wo sie weiter verarbeitet werden ist die Geschwindigkeit recht angenehm :)
Vielen Dank nochmal