OpenCV Error

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
Benutzeravatar
Felix92
User
Beiträge: 133
Registriert: Mittwoch 7. November 2018, 17:57

Huhu und zwar stehe ich mal wieder vor einem Problem :D

Erstmal der Error :

Code: Alles auswählen

py", line 125, in board_area
    gray = cv2.cvtColor(roi_board, cv2.COLOR_BGR2GRAY)
cv2.error: OpenCV(4.1.0) /io/opencv/modules/imgproc/src/color.cpp:182: error: (-215:Assertion failed) !_src.empty() in function 'cvtColor'
Hier der Code:

Code: Alles auswählen

from .media_file import MediaFile
import openshot
import numpy as np
import cv2

class BoardVideo(MediaFile):
    """
    This class contains the video
    """

    def __init__(self, file_path): 
        self.__file_path = file_path
        self.background = None
        self.accumulated_weight = 0.5
        self.visualiser_time = []
        self.blackboard_time = []
        self.subvideos = []

        self.roi_visualiser_top = 250
        self.roi_visualiser_bottom = 600
        self.roi_visualiser_right = 800 
        self.roi_visualiser_left = 1000

        self.roi_board_top = 260 
        self.roi_board_bottom = 140
        self.roi_board_right = 150
        self.roi_board_left = 750

    def get(self):
        return self.__file_path
    
    def calc_accum_avg(self, frame, accumulated_weight):

        self.background

        if self.background is None:
            self.background = frame.copy().astype("float")
            return None
        
        cv2.accumulateWeighted(frame, background, self.accumulated_weight)

    def segment(self, frame, threshold=50):

        self.background

        diff = cv2.absdiff(self.background.astype("uint8"),frame)

        _, thresholded = cv2.threshold(diff, threshold, 255, cv2.THRESH_BINARY)

        contours, hierarchy = cv2.findContours(thresholded.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

        if len(contours) == 0:
            return None
        
        else:
            move_segment = max(contours, key = cv2.contourArea)

            return (thresholded, move_segment)

    def visualiser_area(self): 
        video = self.__file_path
        video = cv2.VideoCapture(str(video))
        #length = int(video.get(cv2.CAP_PROP_FRAME_COUNT))
        num_frames = 0

        fgbg = cv2.createBackgroundSubtractorMOG2()

        while True:
            ret,frame = video.read()

            if frame is None:
                return

            roi_visualiser = frame[self.roi_visualiser_top:self.roi_visualiser_bottom,self.roi_visualiser_right:self.roi_visualiser_left]
            fgmask = fgbg.apply(roi_visualiser)
            gray = cv2.cvtColor(roi_visualiser, cv2.COLOR_BGR2GRAY)
            gray = cv2.GaussianBlur(gray, (9,9), 0)
            #gray = cv2.Canny(gray, 300, 550, 3)
            if num_frames < 1:
                self.calc_accum_avg(gray, self.accumulated_weight)

            else:
                visualiser = self.segment(gray)
                if visualiser is not None:  
                    thresholded, move_segment = visualiser
                    milli = video.get(cv2.CAP_PROP_POS_MSEC)
                    time = milli/1000
                    self.visualiser_time.append(time)
                elif visualiser is None:
                    if not self.visualiser_time:
                        pass
                    else:
                        number = 0
                        start = self.visualiser_time[0]
                        end = self.visualiser_time[-1]
                        clip = openshot.Clip(str(number))
                        clip.Start(start)
                        clip.End(end)
                        self.subvideos.append(clip)
                        self.visualiser_time.clear()
                        number += 1

            num_frames += 1
            #print(self.subvideos)

        video.release()
        cv2.destroyAllWindows()

    def board_area(self):
        video = self.__file_path
        video = cv2.VideoCapture(str(video))
        length = int(video.get(cv2.CAP_PROP_FRAME_COUNT))
        num_frames = 0

        fgbg = cv2.createBackgroundSubtractorMOG2()

        while True:
            ret,frame = video.read()

            if frame is None:
                return

            roi_board = frame[self.roi_board_top:self.roi_board_bottom,self.roi_board_right:self.roi_board_left]
            fgmask = fgbg.apply(roi_board)
            gray = cv2.cvtColor(roi_board, cv2.COLOR_BGR2GRAY)
            gray = cv2.GaussianBlur(gray, (9,9), 0)

            if num_frames < 1:
                self.calc_accum_avg(gray, self.accumulated_weight)

            else:
                board = self.segment(gray)
                if board is not None:  
                    thresholded, move_segment = board
                    milli = video.get(cv2.CAP_PROP_POS_MSEC)
                    time = milli/1000
                    self.board_time.append(time)
                elif board is None:
                    if not self.board_time:
                        pass
                    else:
                        number = 0
                        start = self.board_time[0]
                        end = self.board_time[-1]
                        clip = openshot.Clip("a" + str(number))
                        clip.Start(start)
                        clip.End(end)
                        self.subvideos.append(clip)
                        self.board_time.clear()
                        number += 1

            num_frames += 1

        video.release()
        cv2.destroyAllWindows()
Bei der Methode visualiser_area klappt das wunderbar ..deshalb verstehe ich den Fehler in board_area nicht so ganz wäre super wenn mir da jmd weiterhelfen könnte :)
Ziel ist es in einem Video zu erfassen wenn in 2 Bereichen eine Bewegung (ausgenommen Schatten) statt findet.
Dann wird wenn es eine Bewegung gibt von bis ein Clip erstellt und dieser in einer Liste gespeichert.
MfG Felix
__deets__
User
Beiträge: 14528
Registriert: Mittwoch 14. Oktober 2015, 14:29

Deine Quelle ist leer. Was die Fehlermeldung sehr deutlich wiedergibt.
Benutzeravatar
Felix92
User
Beiträge: 133
Registriert: Mittwoch 7. November 2018, 17:57

Ja das kann aber nicht sein ich habe mir das Video anzeigen lassen und an dieser Stelle ein Rechteck eingezeichnet .. :?:
__deets__
User
Beiträge: 14528
Registriert: Mittwoch 14. Oktober 2015, 14:29

Der Fehler sagt was anderes. Kann nicht sein kann also nicht sein.
Benutzeravatar
Felix92
User
Beiträge: 133
Registriert: Mittwoch 7. November 2018, 17:57

Wieso funktioniert es dann bei visualiser_area() da passiert da nichts anderes
__deets__
User
Beiträge: 14528
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das kann ich dir nicht sagen. Hast du dir dein Bild mal per Print ausgegeben bevor du es da rein stopfst?
__deets__
User
Beiträge: 14528
Registriert: Mittwoch 14. Oktober 2015, 14:29

Und wenn da vermeintlich das gleich passiert, warum machst du das überhaupt? Wenn das nicht so sein sollte, dann wäre das ein heißer Kandidat dafür, warum da ein anderes Ergebnis tun kommt.
Benutzeravatar
__blackjack__
User
Beiträge: 13077
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Felix92: Numpy wird importiert, aber gar nicht verwendet.

Die Klasse `BoardVideo` ist komisch. Als erstes fällt auf das die von `MediaFile` erbt, aber weder die `__init__()` der Basisklasse aufruft, noch irgend etwas anderes von dieser Klasse zu verwenden scheint. Das macht keinen Sinn!

Der Import von `MediaFile` und das vorliegende Modul lassen zudem den Verdacht aufkommen es gibt dort immer ein Modul pro Klasse, oder anders herum in jedem Modul steckt nur eine Klasse. Das macht man in Python nicht so. Module sind dazu da um mehrere zusammnegehörige Funktionen und Klassen zusammen zu fassen, nicht um jede Klasse noch einmal in ein eigenes Modul zu stecken.

Der Docstring von `BoardVideo` ist so inhaltsarm, dass man ihn besser weg lässt.

Die beiden Unterstriche bei `__file_path` sollten nur *ein* Unterstrich sein. Wenn überhaupt.

Die `get()`-Methode sollte dem Leser im Namen vermitteln *was* das geholt wird, also `get_filename()`, oder besser ein Property sein, oder noch besser: man macht das Attribut `filenmame` einfach öffentlich.

In der `__init__()` wird ein Attribut `blackboard_time` angelegt, dass nirgends benutzt wird, dafür versucht `board_area()` auf ein Attribut `board_time` zuzugreifen, dass es gar nicht gibt – ich hätte da so den Verdacht, dass man eines von den beiden Attributen in das andere umbenennen sollte.

So ein `BoardVideo`-Objekt hat ziemlich viele Attribute und man kann zwei nach dem gleichen Muster benannte Teilgruppen identifizieren, die jeweils nur in einer Methode verwendet werden. Und diese beiden Methoden sind auch noch lang und fast identische Kopien, eben bis auf diese Attributgruppen. Das riecht komisch! Das sollten sehr wahrscheinlich keine Attribute sein, sondern Argumente der jeweiligen Methode. Wobei die `*_time`-Listen weder Attribut noch Argument sind, weil die nur innerhalb der jeweiligen Methode verwendet wird, also eigentlich eine lokale Variable ist. Und die jeweils zusammengehörenden `roi_*_*`-Attribute kann man zu einem Wert zusammenfassen.

Nun unterscheiden sich die Methoden `visualiser_area()` und `board_area()` aber inhaltlich gar nicht mehr, das ist also bloss eine Methode die jeweils andere ROI-Werte als Argument bekommt. Kann man also zu einer `area()`-Methode zusammen (und vielleicht einen besseren Namen wählen).

Die beiden Zeilen nach der ``while``-Schleife in den `*_area`-Methoden werden niemals ausgeführt, weil die Schleife nur durch ein ``return`` oder eine Ausnahme verlassen werden kann. Darum gehören solche Aufräumarbeiten in einen ``finally``-Zweig.

Anstatt der ``while True:``-Schleife und dem manuell verwalteten `num_frames` sollte man die aktuelle Frame-Nummer mit einer ``for``-Schleife und `itertools.count()` erstellen.

`fgbg` ist kein guter Name. Das Objekt wird auch gar nicht wirklich genutzt‽

Vom `video.read()`-Aufruf sollte man das erste Argument testen und nicht das zweite. `ret` ist als Name auch nicht gut. `is_ok` vermittelt dem Leser a) das es sich um einen Wahrheitswert handelt, und b) was der bedeutet.

Mit der Rückgabe von `segment()` wird nicht wirklich etwas gemacht ausser sie auf `None` zu testen.

Wobei es keinen Sinn macht im ``if`` auf ``not None`` zu testen um dann im ``elif`` noch einmal explizit auf ``None`` zu testen. Genau so unsinnig ist ein ``if``- oder ``else``-Zweig in dem einfach nur ``pass`` steht, wenn es beide Zweige gibt.

Die Clipnummer `number` ist immer 0. Es wird zwar eins drauf addiert nach dem `number` verwendet wurde, aber vor dem nächsten Clip wird `number` wieder mit 0 belegt.

`calc_accum_avg()` verwendet das zweite Argument überhaupt nicht, dafür wird versucht auf ein nicht vorhandenes, lokales `background` zuzugreifen.

Wenn man eine Funktion oder Methode einfach nur verlassen will, ohne einen expliziten Rückgabewert, dann nimmt man nur ``return``.

Zwuschenstand (ungetestet):

Code: Alles auswählen

from itertools import count

import cv2
import openshot

VISUALISER_ROI_SLICES = (slice(250, 600), slice(800, 1000))
BOARD_ROI_SLICES = (slice(140, 260), slice(150, 750))


class BoardVideo:

    def __init__(self, file_path):
        self.file_path = file_path
        self.background = None
        self.accumulate_weight = 0.5
        self.subvideos = list()

    def calculate_accumulated_average(self, frame):
        if self.background is None:
            self.background = frame.copy().astype('float')
        else:
            cv2.accumulateWeighted(
                frame, self.background, self.accumulate_weight
            )

    def segment(self, frame, threshold=50):
        diff = cv2.absdiff(self.background.astype('uint8'), frame)
        _, thresholded = cv2.threshold(diff, threshold, 255, cv2.THRESH_BINARY)
        contours, _hierarchy = cv2.findContours(
            thresholded.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE
        )
        if len(contours) == 0:
            return None
        else:
            return (thresholded, max(contours, key=cv2.contourArea))

    def area(self, roi_slices, clip_prefix):
        video = cv2.VideoCapture(str(self.file_path))
        try:
            background_subtractor = cv2.createBackgroundSubtractorMOG2()
            times = list()
            clip_numbers = count()
            for frame_number in count():
                is_ok, frame = video.read()
                if not is_ok:
                    break

                roi = frame[roi_slices]
                gray = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY)
                gray = cv2.GaussianBlur(gray, (9, 9), 0)
                foreground_mask = background_subtractor.apply(roi)

                if frame_number == 0:
                    self.calculate_accumulated_average(gray)
                else:
                    if not self.segment(gray):
                        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()
Als nächstes würde ich hier das Lesen der Videodatei in eine Generatorfunktion auslagern. Dann kann man `frame_number` loswerden, das ja nur gebraucht wird um den ersten Frame anders als den Rest zu behandeln.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
Felix92
User
Beiträge: 133
Registriert: Mittwoch 7. November 2018, 17:57

Vielen Dank @__blackjack__ ja Mediafile macht bis jetzt noch nichts das ist korrekt da soll vermutlich noch was kommen laut Softwarearchitekt (Master) daher sollte ich das so umsetzen :)
Danke nochmal für die ausführliche Antwort das hilft mir echt weiter :mrgreen:
Antworten