Пример #1
0
class VideoAnnotator(QtGui.QWidget):
    def __init__(self):
        super(VideoAnnotator, self).__init__()

        self.vid = None

        self.pic = None
        self.sld = None
        self.sld2 = None

        self.ranges = None

        # Number of distinct viewable video positions (slider granularity)
        self.num_positions = 50000

        self.initUI()

    def initUI(self):
        vbox = QtGui.QVBoxLayout()

        # Create the widget that displays the video frame
        self.pic = ImageWidget()

        # A horizontal slider to scroll through the video
        self.sld = QtGui.QSlider(QtCore.Qt.Horizontal)
        # self.sld.setFocusPolicy(QtCore.Qt.NoFocus)
        self.sld.setRange(0, self.num_positions)
        self.sld.setValue(100)
        self.sld.valueChanged[int].connect(self.change_slider_value)

        # A special widget that displays the marked ranges and average frame colors
        # (Is also called slider, because it should later take over the role of the conventional slider)
        self.sld2 = SliderWidget()

        # The ranges class manages the marked video ranges and sends them to the widget when changed
        self.ranges = Ranges(update_function=self.sld2.setRanges)
        self.load_or_save_ranges(save=False)

        # A label with a quick introduction
        man_label = QtGui.QLabel()
        man_label.setText(
            "Use the slider to navigate (Mouse + keyboard).\n" +
            "Q = Start preview range, A = Start ad range, Z = Start ignore range\n"
            +
            "D = End current range, Bksp = remove last element.\n Auto-saves on close."
        )
        man_label.setSizePolicy(QtGui.QSizePolicy.Minimum,
                                QtGui.QSizePolicy.Fixed)

        # Put everything together and show it
        vbox.addWidget(self.pic)
        vbox.addWidget(self.sld)
        vbox.addWidget(self.sld2)
        vbox.addWidget(man_label)

        self.setLayout(vbox)
        self.setWindowTitle('Video Annotator')
        self.setAcceptDrops(True)
        self.show()

    def current_position(self, slider_value=None):
        if slider_value is None:
            return self.sld.value() / self.num_positions
        else:
            return slider_value / self.num_positions

    def change_slider_value(self, value):
        if self.vid is not None:
            self.pic.setImage(self.vid.get_frame(self.current_position(value)))

    def load_video(self, filename):
        self.vid = Video(filename)
        self.vid.compute_averages(50)
        #self.vid.compute_averages(1000)

        self.pic.setImage(self.vid.get_frame(0.0))
        self.sld2.setAverages(self.vid.get_averages())
        self.ranges = Ranges(update_function=self.sld2.setRanges)
        self.load_or_save_ranges(save=False)
        self.sld2.setRanges(self.ranges.get_ranges())

    def keyPressEvent(self, e):
        if e.key() == QtCore.Qt.Key_Escape:
            self.close()
        elif e.key() == QtCore.Qt.Key_Backspace:
            self.ranges.remove_last_element()
        elif e.key() == QtCore.Qt.Key_A:
            self.ranges.add_range_start('ad', self.current_position())
        elif e.key() == QtCore.Qt.Key_Q:
            self.ranges.add_range_start('preview', self.current_position())
        elif e.key() == QtCore.Qt.Key_Z:
            self.ranges.add_range_start('ignore', self.current_position())
        elif e.key() == QtCore.Qt.Key_D:
            self.ranges.add_range_end(self.current_position())

    def dragEnterEvent(self, event):
        if event.mimeData().hasUrls():
            event.accept()
        else:
            event.ignore()

    def dropEvent(self, event):
        url = event.mimeData().urls()[0]
        path = str(url.toLocalFile())
        if os.path.isfile(path):
            # Save old ranges
            self.load_or_save_ranges(save=True)
            # Open new video
            self.load_video(path)

    def load_or_save_ranges(self, save=False):
        if self.vid is not None:
            filename = os.path.splitext(
                self.vid.get_filename())[0] + '_annotation.txt'
            if save:
                with open(filename, 'w') as f:
                    json.dump(self.ranges.get_ranges(), f)
            else:
                if os.path.isfile(filename):
                    with open(filename, 'r') as f:
                        range_data = json.load(f)
                        self.ranges.set_ranges(range_data)

    def closeEvent(self, e):
        self.load_or_save_ranges(save=True)