def __init__(self): super(Example1, self).__init__('dir examples') self.parent = None self._directory = ControlDir('Choose a directory') self._file = ControlFile('Choose a file') self._filetree = ControlFilesTree('Choose a file') self._image = ControlImage('Image') self._boundaries = ControlBoundingSlider('Bounding', horizontal=True) self._button = ControlButton('Click') self._button.value = self.onButtonClick # self._directory.value=self.onButtonClick self._checkbox = ControlCheckBox('Choose a directory') self._checkboxList = ControlCheckBoxList('Choose a file') self._player = ControlPlayer('Choose a file') self._slider = ControlSlider('Slider') self._player.show() self._checkboxList.value = [('Item 1', True), ('Item 2', False), ('Item 3', True)] self._combobox = ControlCombo('Choose a item') self._list = ControlList('List label') self._progress = ControlProgress('Progress bar') self._visvisVolume = ControlVisVisVolume('Visvis') self._timeline = ControlEventTimeline('Timeline') self._combobox.add_item('Item 1', 'Value 1') self._combobox.add_item('Item 2', 'Value 2') self._combobox.add_item('Item 3', 'Value 3') self._combobox.add_item('Item 4') self._list.value = [('Item1', 'Item2', 'Item3',), ('Item3', 'Item4', 'Item5',)] imageWithVolume = np.zeros((100, 100, 100), np.uint8) imageWithVolume[30:40, 30:50, :] = 255 imageWithVolume[30:40, 70:72, :] = 255 self._visvisVolume.value = imageWithVolume self._visvis = ControlVisVis('Visvis') values1 = [(i, random.random(), random.random()) for i in range(130)] values2 = [(i, random.random(), random.random()) for i in range(130)] self._visvis.value = [values1, values2] self.formset = [ '_visvis' , '_directory' , '_button' , '_file' , '_boundaries' , '_filetree' , '_image' , '_slider' , ('_checkboxList', '_player') , ('_checkbox', ' ') , ('_combobox', ' ') , '_progress' , '=' , ('_visvisVolume', '||', '_list') , '_timeline' ]
def __init__(self): global conf conf += 'pythonvideoannotator.resources' # Resources can only be loaded after pyqt is running super(BaseModule, self).__init__('Video annotation editor') self._project = Project(parent=self) Dialog.project = self._project self._player = ControlPlayer("Player") self._time = ControlEventTimeline('Time') self._dock = ControlDockWidget("Timeline", side='bottom', order=1, margin=5) self.formset = ['_player'] self._dock.value = self._time self._player.process_frame_event = self.process_frame_event self._player.click_event = self.on_player_click_event self._time.key_release_event = self.__timeline_key_release_event self.load_order = [] self.mainmenu.insert( 0, { 'File': [{ 'Open': self.__open_project_event, 'icon': conf.ANNOTATOR_ICON_OPEN }, '-', { 'Save': self.__save_project_event, 'icon': conf.ANNOTATOR_ICON_SAVE }, { 'Save as': self.__save_project_as_event, 'icon': conf.ANNOTATOR_ICON_SAVE }, '-', { 'Exit': QApplication.closeAllWindows, 'icon': conf.ANNOTATOR_ICON_EXIT }] }) self.mainmenu.insert(1, {'Modules': []}) self.mainmenu.insert(2, {'Windows': []}) track_user_stats()
class BaseModule(BaseWidget): """Application form""" def __init__(self): global conf conf += 'pythonvideoannotator.resources' # Resources can only be loaded after pyqt is running super(BaseModule, self).__init__('Video annotation editor') self._project = Project(parent=self) Dialog.project = self._project self._player = ControlPlayer("Player") self._time = ControlEventTimeline('Time') self._dock = ControlDockWidget("Timeline", side='bottom', order=1, margin=5) self.formset = ['_player'] self._dock.value = self._time self._player.process_frame_event = self.process_frame_event self._player.click_event = self.on_player_click_event self._time.key_release_event = self.__timeline_key_release_event self.load_order = [] self.mainmenu.insert( 0, { 'File': [{ 'Open': self.__open_project_event, 'icon': conf.ANNOTATOR_ICON_OPEN }, '-', { 'Save': self.__save_project_event, 'icon': conf.ANNOTATOR_ICON_SAVE }, { 'Save as': self.__save_project_as_event, 'icon': conf.ANNOTATOR_ICON_SAVE }, '-', { 'Exit': QApplication.closeAllWindows, 'icon': conf.ANNOTATOR_ICON_EXIT }] }) self.mainmenu.insert(1, {'Modules': []}) self.mainmenu.insert(2, {'Windows': []}) ###################################################################################### #### FUNCTIONS ####################################################################### ###################################################################################### def init_form(self): super(BaseModule, self).init_form() if conf.CHART_FILE_PATH: self._time.import_chart(*conf.CHART_FILE_PATH) if conf.PROJECT_PATH: self.load_project(conf.PROJECT_PATH) ###################################################################################### #### IO FUNCTIONS #################################################################### ###################################################################################### def save(self, data, project_path=None): self._project.save(data, project_path) return data def load(self, data, project_path=None): self._project.load(data, project_path) def save_project(self, project_path=None): try: if project_path is None: project_path = QFileDialog.getExistingDirectory( self, "Select the project directory") if project_path is not None and str(project_path) != '': project_path = str(project_path) self.save({}, project_path) except Exception as e: QMessageBox.critical(self, "Error", str(e)) def load_project(self, project_path=None): if project_path is None: project_path = QFileDialog.getExistingDirectory( self, "Select the project directory") if project_path is not None and str(project_path) != '': self.load({}, str(project_path)) ###################################################################################### #### EVENTS ########################################################################## ###################################################################################### def on_player_click_event(self, event, x, y): """ Code to select a blob with the mouse """ super(VideoAnnotationEditor, self).on_player_click_event(event, x, y) self._player.refresh() def process_frame_event(self, frame): """ Function called before render each frame """ return frame def __open_project_event(self): self.load_project() def __save_project_event(self): self.save_project(self._project.directory) def __save_project_as_event(self): self.save_project() def __timeline_key_release_event(self, event): """ Control video playback using the space bar to Play/Pause """ if event.key() == QtCore.Qt.Key_Space: self._player.stop( ) if self._player.is_playing else _player._video.play() ###################################################################################### #### PROPERTIES ###################################################################### ###################################################################################### @property def timeline(self): return self._time @property def player(self): return self._player @property def video(self): return self._player.value @video.setter def video(self, value): self._player.value = value self._player.enabled = value is not None if value: self._time.max = self._player.max @property def project(self): return self._project
def __init__(self): global conf conf += 'pythonvideoannotator.resources' # Resources can only be loaded after pyqt is running super().__init__('Video annotation editor') self._project = Project(parent=self) Dialog.project = self._project self._player = ControlPlayer("Player") self._time = ControlEventTimeline('Time') self._dock = ControlDockWidget("Timeline", side='bottom', order=1, margin=5) self._progress = ControlProgress('Progress', visible=False) # define the application toolbar self.toolbar = [ ControlButton('Open', icon=conf.ANNOTATOR_ICON_OPEN, default=self.__open_project_event), ControlButton('Save', icon=conf.ANNOTATOR_ICON_SAVE, default=self.__save_project_event) ] self.formset = ['_player', '_progress'] self._dock.value = self._time self._player.process_frame_event = self.process_frame_event self._player.click_event = self.on_player_click_event self._player.double_click_event = self.on_player_double_click_event self._player.drag_event = self.on_player_drag_event self._player.end_drag_event = self.on_player_end_drag_event # ignore these controls key release event self._time.key_release_event = lambda x: x self._player.key_release_event = lambda x: x self.load_order = [] self.mainmenu.insert( 0, { 'File': [{ 'Open': self.__open_project_event, 'icon': conf.ANNOTATOR_ICON_OPEN }, '-', { 'Save': self.__save_project_event, 'icon': conf.ANNOTATOR_ICON_SAVE }, { 'Save as': self.__save_project_as_event, 'icon': conf.ANNOTATOR_ICON_SAVE }, '-', { 'Exit': QApplication.closeAllWindows, 'icon': conf.ANNOTATOR_ICON_EXIT }] }) self.mainmenu.insert(1, {'Modules': []}) self.mainmenu.insert(2, {'Windows': []}) track_user_stats() ######################################################################## ###### CHECK NEW VERSIONS RELEASES ##################################### ######################################################################## try: versions = pypi_xmlrpc.package_releases('Python-video-annotator') if versions is not None: new_version = versions[0] if float(new_version) > float(__version__): response = self.question( "<h2>New version <b>[{0}]</b> available</h2>" "<p>Do you wish to update the software?</p>" "<p>The software can be updated later by running the next command in the terminal:</p>" "<i>pip install python-video-annotator --force-reinstall</i>" .format(new_version), 'New version [{0}]'.format(new_version)) if response == 'yes': subprocess.call([ sys.executable, "-m", "pip", "install", 'python-video-annotator', '--force-reinstall' ]) self.message( 'The software was updated and this session will be closed. Please execute the software again.', 'Restart required') exit() else: print('Unable to check new versions') except Exception as e: print('Unable to check new versions:')
class Base(BaseWidget): """Application form""" def __init__(self): global conf conf += 'pythonvideoannotator.resources' # Resources can only be loaded after pyqt is running super().__init__('Video annotation editor') self._project = Project(parent=self) Dialog.project = self._project self._player = ControlPlayer("Player") self._time = ControlEventTimeline('Time') self._dock = ControlDockWidget("Timeline", side='bottom', order=1, margin=5) self._progress = ControlProgress('Progress', visible=False) # define the application toolbar self.toolbar = [ ControlButton('Open', icon=conf.ANNOTATOR_ICON_OPEN, default=self.__open_project_event), ControlButton('Save', icon=conf.ANNOTATOR_ICON_SAVE, default=self.__save_project_event) ] self.formset = ['_player', '_progress'] self._dock.value = self._time self._player.process_frame_event = self.process_frame_event self._player.click_event = self.on_player_click_event self._player.double_click_event = self.on_player_double_click_event self._player.drag_event = self.on_player_drag_event self._player.end_drag_event = self.on_player_end_drag_event # ignore these controls key release event self._time.key_release_event = lambda x: x self._player.key_release_event = lambda x: x self.load_order = [] self.mainmenu.insert( 0, { 'File': [{ 'Open': self.__open_project_event, 'icon': conf.ANNOTATOR_ICON_OPEN }, '-', { 'Save': self.__save_project_event, 'icon': conf.ANNOTATOR_ICON_SAVE }, { 'Save as': self.__save_project_as_event, 'icon': conf.ANNOTATOR_ICON_SAVE }, '-', { 'Exit': QApplication.closeAllWindows, 'icon': conf.ANNOTATOR_ICON_EXIT }] }) self.mainmenu.insert(1, {'Modules': []}) self.mainmenu.insert(2, {'Windows': []}) track_user_stats() ######################################################################## ###### CHECK NEW VERSIONS RELEASES ##################################### ######################################################################## try: versions = pypi_xmlrpc.package_releases('Python-video-annotator') if versions is not None: new_version = versions[0] if float(new_version) > float(__version__): response = self.question( "<h2>New version <b>[{0}]</b> available</h2>" "<p>Do you wish to update the software?</p>" "<p>The software can be updated later by running the next command in the terminal:</p>" "<i>pip install python-video-annotator --force-reinstall</i>" .format(new_version), 'New version [{0}]'.format(new_version)) if response == 'yes': subprocess.call([ sys.executable, "-m", "pip", "install", 'python-video-annotator', '--force-reinstall' ]) self.message( 'The software was updated and this session will be closed. Please execute the software again.', 'Restart required') exit() else: print('Unable to check new versions') except Exception as e: print('Unable to check new versions:') ###################################################################################### #### FUNCTIONS ####################################################################### ###################################################################################### def init_form(self): super().init_form() if conf.CHART_FILE_PATH: self._time.import_chart(*conf.CHART_FILE_PATH) if conf.VIDEOANNOTATOR_PROJECTPATH: self.load_project(conf.VIDEOANNOTATOR_PROJECTPATH) if len(sys.argv) > 1: QTimer.singleShot(1000, self.__load_project_from_argv) ###################################################################################### #### EVENTS ########################################################################## ###################################################################################### def on_player_drag_event(self, p1, p2): if self._project: self._project.player_on_drag(p1, p2) self._player.refresh() def on_player_end_drag_event(self, p1, p2): if self._project: self._project.player_on_end_drag(p1, p2) self._player.refresh() def on_player_click_event(self, event, x, y): """ Code to select a blob with the mouse """ if self._project: self._project.player_on_click(event, x, y) self._player.refresh() def on_player_double_click_event(self, event, x, y): """ Code to select a blob with the mouse """ if self._project: self._project.player_on_double_click(event, x, y) self._player.refresh() def process_frame_event(self, frame): """ Function called before render each frame """ return frame def add_dataset_event(self, dataset): pass def removed_dataset_event(self, dataset): pass def removed_object_event(self, obj): pass def __open_project_event(self): self.load_project() def __save_project_event(self): self.save_project(self._project.directory) def __save_project_as_event(self): self.save_project() def __load_project_from_argv(self): self.load_project(sys.argv[-1]) ###################################################################################### #### EVENT FUNCTIONS ################################################################# ###################################################################################### def select_next_path(self): selected = self.project.tree.selected_item if selected is not None: # If it's a video, try to select its first object and the object's first child if isinstance(selected.win, Video): if selected.childCount() > 0: child_object = selected.child(0) if child_object.childCount() > 0: self.project.tree.selected_item = child_object.child(0) # If it's an object, try to select it's first child elif isinstance(selected.win, Object2D): if selected.childCount() > 0: self.project.tree.selected_item = selected.child(0) # If it's a path try to select the first child of the next object of their parent video elif isinstance(selected.win, Path): parent_object = selected.parent() parent_video = parent_object.parent() parent_object_index = parent_video.indexOfChild(parent_object) if parent_object_index < parent_video.childCount() - 1: next_object = parent_video.child( parent_video.indexOfChild(parent_object) + 1) if next_object.childCount() > 0: self.project.tree.selected_item = next_object.child(0) # If it's the last object of the video, go back to the path of the first one else: next_object = parent_video.child(0) if next_object.childCount() > 0: self.project.tree.selected_item = next_object.child(0) ###################################################################################### ###################################################################################### #### PROPERTIES ###################################################################### ###################################################################################### @property def progress_bar(self): return self._progress @property def timeline(self): return self._time @property def player(self): return self._player @property def video(self): return self._player.value @video.setter def video(self, value): self._player.value = value self._player.enabled = value is not None if value: self._time.max = self._player.max @property def project(self): return self._project
def __init__(self): global conf; conf += 'pythonvideoannotator.resources' # Resources can only be loaded after pyqt is running super(BaseModule, self).__init__('Video annotation editor') self._project = Project(parent=self) Dialog.project = self._project self._player = ControlPlayer("Player") self._time = ControlEventTimeline('Time') self._dock = ControlDockWidget("Timeline", side='bottom', order=1, margin=5) self.formset = ['_player'] self._dock.value = self._time self._player.process_frame_event = self.process_frame_event self._player.click_event = self.on_player_click_event self._time.key_release_event = lambda x: x self._player.key_release_event = lambda x: x self.load_order = [] self.mainmenu.insert(0, {'File': [ {'Open': self.__open_project_event, 'icon': conf.ANNOTATOR_ICON_OPEN}, '-', {'Save': self.__save_project_event , 'icon': conf.ANNOTATOR_ICON_SAVE}, {'Save as': self.__save_project_as_event, 'icon': conf.ANNOTATOR_ICON_SAVE}, '-', {'Exit': QApplication.closeAllWindows, 'icon': conf.ANNOTATOR_ICON_EXIT} ] } ) self.mainmenu.insert(1, {'Modules': []} ) self.mainmenu.insert(2, {'Windows': []} ) track_user_stats() ######################################################################## ###### CHECK NEW VERSIONS RELEASES ##################################### ######################################################################## try: versions = pypi_xmlrpc.package_releases('Python-video-annotator') if versions is not None: new_version = versions[0] if float(new_version) > float(__version__): response = self.question( "<h2>New version <b>[{0}]</b> available</h2>" "<p>Do you wish to update the software?</p>" "<p>The software can be updated later by running the next command in the terminal:</p>" "<i>pip install python-video-annotator --force-reinstall</i>".format(new_version), 'New version [{0}]'.format(new_version) ) if response == 'yes': subprocess.call([sys.executable, "-m", "pip", "install", 'python-video-annotator', '--force-reinstall']) self.message('The software was updated and this session will be closed. Please execute the software again.', 'Restart required') exit() else: print('Enabled to check new versions') except Exception as e: print('Enabled to check new versions:')
class BaseModule(BaseWidget): """Application form""" def __init__(self): global conf; conf += 'pythonvideoannotator.resources' # Resources can only be loaded after pyqt is running super(BaseModule, self).__init__('Video annotation editor') self._project = Project(parent=self) Dialog.project = self._project self._player = ControlPlayer("Player") self._time = ControlEventTimeline('Time') self._dock = ControlDockWidget("Timeline", side='bottom', order=1, margin=5) self.formset = ['_player'] self._dock.value = self._time self._player.process_frame_event = self.process_frame_event self._player.click_event = self.on_player_click_event self._time.key_release_event = lambda x: x self._player.key_release_event = lambda x: x self.load_order = [] self.mainmenu.insert(0, {'File': [ {'Open': self.__open_project_event, 'icon': conf.ANNOTATOR_ICON_OPEN}, '-', {'Save': self.__save_project_event , 'icon': conf.ANNOTATOR_ICON_SAVE}, {'Save as': self.__save_project_as_event, 'icon': conf.ANNOTATOR_ICON_SAVE}, '-', {'Exit': QApplication.closeAllWindows, 'icon': conf.ANNOTATOR_ICON_EXIT} ] } ) self.mainmenu.insert(1, {'Modules': []} ) self.mainmenu.insert(2, {'Windows': []} ) track_user_stats() ######################################################################## ###### CHECK NEW VERSIONS RELEASES ##################################### ######################################################################## try: versions = pypi_xmlrpc.package_releases('Python-video-annotator') if versions is not None: new_version = versions[0] if float(new_version) > float(__version__): response = self.question( "<h2>New version <b>[{0}]</b> available</h2>" "<p>Do you wish to update the software?</p>" "<p>The software can be updated later by running the next command in the terminal:</p>" "<i>pip install python-video-annotator --force-reinstall</i>".format(new_version), 'New version [{0}]'.format(new_version) ) if response == 'yes': subprocess.call([sys.executable, "-m", "pip", "install", 'python-video-annotator', '--force-reinstall']) self.message('The software was updated and this session will be closed. Please execute the software again.', 'Restart required') exit() else: print('Enabled to check new versions') except Exception as e: print('Enabled to check new versions:') ###################################################################################### #### FUNCTIONS ####################################################################### ###################################################################################### def init_form(self): super(BaseModule, self).init_form() if conf.CHART_FILE_PATH: self._time.import_chart(*conf.CHART_FILE_PATH) if conf.PROJECT_PATH: self.load_project(conf.PROJECT_PATH) ###################################################################################### #### IO FUNCTIONS #################################################################### ###################################################################################### def save(self, data, project_path=None): self._project.save(data, project_path) return data def load(self, data, project_path=None): try: self._project.load(data, project_path) except FileNotFoundError as e: QMessageBox.critical(self, "Error", str(e)) def save_project(self, project_path=None): try: if project_path is None: project_path = QFileDialog.getExistingDirectory(self, "Select the project directory") if project_path is not None and str(project_path)!='': project_path = str(project_path) self.save({}, project_path) except Exception as e: QMessageBox.critical(self, "Error", str(e)) def load_project(self, project_path=None): if project_path is None: project_path = QFileDialog.getExistingDirectory(self, "Select the project directory") if project_path is not None and str(project_path)!='': self.load({}, str(project_path) ) ###################################################################################### #### EVENTS ########################################################################## ###################################################################################### def on_player_click_event(self, event, x, y): """ Code to select a blob with the mouse """ super(VideoAnnotationEditor, self).on_player_click_event(event, x, y) self._player.refresh() def process_frame_event(self, frame): """ Function called before render each frame """ return frame def add_dataset_event(self, dataset): pass def removed_dataset_event(self, dataset): pass def removed_object_event(self, obj): pass def __open_project_event(self): self.load_project() def __save_project_event(self): print('Project saved') self.save_project(self._project.directory) def __save_project_as_event(self): self.save_project() def keyReleaseEvent(self, event): ###################################################################################### #### SPECIFIC SHORTCUTS############################################################### ###################################################################################### #Go to the next event and then click the mark the point button if event.key() == QtCore.Qt.Key_I: if self.timeline.timeline_widget.selected is not None and self.timeline.timeline_widget.selected != self.timeline.timeline_widget.pointer: self.move_to_next_event() self.mark_point() ###################################################################################### #### DOCK SHORTCUTS ################################################################## ###################################################################################### #Select the path of the next object if event.key() == QtCore.Qt.Key_U: self.select_next_path() #"Click" the Mark Point button in the current Path elif event.key() == QtCore.Qt.Key_O: self.mark_point() ###################################################################################### #### TIMELINE SHORTCUTS ############################################################## ###################################################################################### if self.timeline.timeline_widget.selected is not None: modifier = int(event.modifiers()) if modifier == QtCore.Qt.ControlModifier and event.key() == QtCore.Qt.Key_Left: self.move_event_or_pointer_left(event) if modifier == QtCore.Qt.ControlModifier and event.key() == QtCore.Qt.Key_Right: self.move_event_or_pointer_right(event) if self.timeline.timeline_widget.selected != self.timeline.timeline_widget.pointer: # Delete the selected event if event.key() == QtCore.Qt.Key_Delete: self.timeline.timeline_widget.remove_selected_event() # Lock or unlock an event if event.key() == QtCore.Qt.Key_L: self.timeline.timeline_widget.toggle_selected_event_lock() if event.key() == QtCore.Qt.Key_E: self.move_to_next_event() if event.key() == QtCore.Qt.Key_Q: self.move_to_previous_event() if modifier == QtCore.Qt.ControlModifier and event.key() == QtCore.Qt.Key_Up: self.move_event_up() if modifier == QtCore.Qt.ControlModifier and event.key() == QtCore.Qt.Key_Down: self.move_event_down() if modifier == int( QtCore.Qt.ShiftModifier | QtCore.Qt.ControlModifier) and event.key() == QtCore.Qt.Key_Left: self.move_event_end_left() if modifier == int( QtCore.Qt.ShiftModifier | QtCore.Qt.ControlModifier) and event.key() == QtCore.Qt.Key_Right: self.move_event_end_right() if modifier == QtCore.Qt.ShiftModifier and event.key() == QtCore.Qt.Key_Left: self.move_event_begin_left() if modifier == QtCore.Qt.ShiftModifier and event.key() == QtCore.Qt.Key_Right: self.move_event_begin_right() else: if event.key() == QtCore.Qt.Key_S: self.create_event_at_current_frame() # walk backwards 1 step elif event.key() == QtCore.Qt.Key_A: self.timeline.timeline_widget.position = self.timeline.timeline_widget.position - 1 # forward 1 step elif event.key() == QtCore.Qt.Key_D: self.timeline.timeline_widget.position = self.timeline.timeline_widget.position + 1 elif event.key() == QtCore.Qt.Key_E: self.move_to_first_event() elif event.key() == QtCore.Qt.Key_Q: self.move_to_last_event() ###################################################################################### #### PLAYER SHORTCUTS ################################################################ ###################################################################################### # Control video playback using the space bar to Play/Pause if event.key() == QtCore.Qt.Key_Space: if self.player.video_widget.control.is_playing: self.player.video_widget.control.stop() else: self.player.video_widget.control.play() # Jumps 1 frame backwards elif event.key() == QtCore.Qt.Key_A: self.player.video_widget.control.video_index -= 2 self.player.video_widget.control.call_next_frame() # Jumps 1 frame forward elif event.key() == QtCore.Qt.Key_D: self.player.video_widget.control.call_next_frame() # Jumps 20 seconds backwards elif event.key() == QtCore.Qt.Key_Z: self.player.video_widget.control.jump_backward() self.player.video_widget.control.call_next_frame() # Jumps 20 seconds forward elif event.key() == QtCore.Qt.Key_C: self.player.video_widget.control.jump_forward() self.player.video_widget.control.call_next_frame() elif event.key() == QtCore.Qt.Key_M: self._move_img = False elif event.key() == QtCore.Qt.Key_1: self.player.video_widget.control.next_frame_step = 1 self.player.video_widget.show_tmp_msg('Speed: 1x') elif event.key() == QtCore.Qt.Key_2: self.player.video_widget.control.next_frame_step = 2 self.player.video_widget.show_tmp_msg('Speed: 2x') elif event.key() == QtCore.Qt.Key_3: self.player.video_widget.control.next_frame_step = 3 self.player.video_widget.show_tmp_msg('Speed: 3x') elif event.key() == QtCore.Qt.Key_4: self.player.video_widget.control.next_frame_step = 4 self.player.video_widget.show_tmp_msg('Speed: 4x') elif event.key() == QtCore.Qt.Key_5: self.player.video_widget.control.next_frame_step = 5 self.player.video_widget.show_tmp_msg('Speed: 5x') elif event.key() == QtCore.Qt.Key_6: self.player.video_widget.control.next_frame_step = 6 self.player.video_widget.show_tmp_msg('Speed: 6x') elif event.key() == QtCore.Qt.Key_7: self.player.video_widget.control.next_frame_step = 7 self.player.video_widget.show_tmp_msg('Speed: 7x') elif event.key() == QtCore.Qt.Key_8: self.player.video_widget.control.next_frame_step = 8 self.player.video_widget.show_tmp_msg('Speed: 8x') elif event.key() == QtCore.Qt.Key_9: self.player.video_widget.control.next_frame_step = 9 self.player.video_widget.show_tmp_msg('Speed: 9x') ###################################################################################### #### EVENT FUNCTIONS ################################################################# ###################################################################################### def select_next_path(self): selected = self.project.tree.selected_item if selected is not None: #If it's a video, try to select its first object and the object's first child if isinstance(selected.win, Video): if selected.childCount() > 0: child_object = selected.child(0) if child_object.childCount() > 0: self.project.tree.selected_item = child_object.child(0) #If it's an object, try to select it's first child elif isinstance(selected.win, Object2D): if selected.childCount() > 0: self.project.tree.selected_item = selected.child(0) #If it's a path try to select the first child of the next object of their parent video elif isinstance(selected.win, Path): parent_object = selected.parent() parent_video = parent_object.parent() parent_object_index = parent_video.indexOfChild(parent_object) if parent_object_index < parent_video.childCount() -1 : next_object = parent_video.child(parent_video.indexOfChild(parent_object)+1) if next_object.childCount() > 0: self.project.tree.selected_item = next_object.child(0) #If it's the last object of the video, go back to the path of the first one else: next_object = parent_video.child(0) if next_object.childCount() > 0: self.project.tree.selected_item = next_object.child(0) def mark_point(self): selected = self.project.tree.selected_item if selected is not None and isinstance(selected.win, Path): path = selected.win path.mark_point_button.click() def move_to_next_event(self): index = self.timeline.timeline_widget.selected_row.events.index(self.timeline.timeline_widget.selected) if index < len(self.timeline.timeline_widget.selected_row.events)-1: self.timeline.timeline_widget.selected = self.timeline.timeline_widget.selected_row.events[index+1] self.timeline.timeline_widget.position = self.timeline.timeline_widget.selected.begin def move_to_previous_event(self): index = self.timeline.timeline_widget.selected_row.events.index(self.timeline.timeline_widget.selected) if index > 0: self.timeline.timeline_widget.selected = self.timeline.timeline_widget.selected_row.events[index - 1] self.timeline.timeline_widget.position = self.timeline.timeline_widget.selected.begin def move_to_first_event(self): if self.timeline.timeline_widget.selected_row is not None and len(self.timeline.timeline_widget.selected_row)>0: self.timeline.timeline_widget.selected = self.timeline.timeline_widget.selected_row.events[0] self.timeline.timeline_widget.position = self.timeline.timeline_widget.selected.begin def move_to_last_event(self): if self.timeline.timeline_widget.selected_row is not None and len(self.timeline.timeline_widget.selected_row)>0: self.timeline.timeline_widget.selected = self.timeline.timeline_widget.selected_row.events[len(self.timeline.timeline_widget.selected_row)-1] self.timeline.timeline_widget.position = self.timeline.timeline_widget.selected.begin def move_event_or_pointer_left(self, event): modifier = int(event.modifiers()) self.timeline.timeline_widget.selected.move(-1, 0) event.ignore() self.timeline.timeline_widget.repaint() def move_event_or_pointer_right(self, event): modifier = int(event.modifiers()) self.timeline.timeline_widget.selected.move(1, 0) event.ignore() self.timeline.timeline_widget.repaint() def move_event_up(self): self.timeline.timeline_widget.selected.move(0, self.timeline.timeline_widget.selected.top_coordinate - self.timeline.timeline_widget.TRACK_HEIGHT) self.timeline.timeline_widget.repaint() def move_event_down(self): self.timeline.timeline_widget.selected.move(0, self.timeline.timeline_widget.selected.top_coordinate + self.timeline.timeline_widget.TRACK_HEIGHT) self.timeline.timeline_widget.repaint() def move_event_end_left(self): self.timeline.timeline_widget.selected.move_end(-1) self.timeline.timeline_widget.repaint() def move_event_end_right(self): self.timeline.timeline_widget.selected.move_end(1) self.timeline.timeline_widget.repaint() def move_event_begin_left(self): self.timeline.timeline_widget.selected.move_begin(-1) self.timeline.timeline_widget.repaint() def move_event_begin_right(self): self.timeline.timeline_widget.selected.move_begin(1) self.timeline.timeline_widget.repaint() def create_event_at_current_frame(self): if not self.timeline.timeline_widget.creating_event: # Start self.timeline.timeline_widget.creating_event_start = self.timeline.timeline_widget.pointer.frame self.timeline.timeline_widget.creating_event = True # TODO Add some indicator that an event is being recorded, like # using the track selector circle to become red else: # End, must be followed right after Start key and have no # effect otherwise self.timeline.timeline_widget.creating_event_end = self.timeline.timeline_widget.pointer.frame start = self.timeline.timeline_widget.creating_event_start end = self.timeline.timeline_widget.creating_event_end comment = "" if end > start: track = self.timeline.timeline_widget.selected_row if track is None and len(self.timeline.timeline_widget.tracks)>0: track = self.timeline.timeline_widget.tracks[0] if track is None: track = self.timeline.timeline_widget.add_track() self.timeline.timeline_widget.add_event(start, end, comment, track=track ) self.timeline.timeline_widget.repaint() self.timeline.timeline_widget.creating_event = False else: self.timeline.timeline_widget.creating_event = False ###################################################################################### #### PROPERTIES ###################################################################### ###################################################################################### @property def timeline(self): return self._time @property def player(self): return self._player @property def video(self): return self._player.value @video.setter def video(self, value): self._player.value = value self._player.enabled = value is not None if value: self._time.max = self._player.max @property def project(self): return self._project
def __init__(self): global conf conf += 'pythonvideoannotator.resources' # Resources can only be loaded after pyqt is running super(BaseModule, self).__init__('Video annotation editor') self._project = Project(parent=self) Dialog.project = self._project self._player = ControlPlayer("Player") self._time = ControlEventTimeline('Time') self._dock = ControlDockWidget("Timeline", side='bottom', order=1, margin=5) self.formset = ['_player'] self._dock.value = self._time self._player.process_frame_event = self.process_frame_event self._player.click_event = self.on_player_click_event self._time.key_release_event = self.__timeline_key_release_event self.load_order = [] self.mainmenu.insert( 0, { 'File': [{ 'Open': self.__open_project_event, 'icon': conf.ANNOTATOR_ICON_OPEN }, '-', { 'Save': self.__save_project_event, 'icon': conf.ANNOTATOR_ICON_SAVE }, { 'Save as': self.__save_project_as_event, 'icon': conf.ANNOTATOR_ICON_SAVE }, '-', { 'Exit': QApplication.closeAllWindows, 'icon': conf.ANNOTATOR_ICON_EXIT }] }) self.mainmenu.insert(1, {'Modules': []}) self.mainmenu.insert(2, {'Windows': []}) track_user_stats() ######################################################################## ###### CHECK NEW VERSIONS RELEASES ##################################### ######################################################################## try: versions = pypi_xmlrpc.package_releases('Python-video-annotator') if versions is not None: new_version = versions[0] new_version_numbers = [int(x) for x in new_version.split('.')] version_numbers = [int(x) for x in __version__.split('.')] for new_n, n in zip(new_version_numbers, version_numbers): if new_n > n: response = self.question( "<h2>New version <b>[{0}]</b> available</h2>" "<p>Do you wish to update the software?</p>" "<p>The software can be updated later by running the next command in the terminal:</p>" "<i>pip install python-video-annotator --force-reinstall</i>" .format(new_version), 'New version [{0}]'.format(new_version)) if response == 'yes': subprocess.call([ sys.executable, "-m", "pip", "install", 'python-video-annotator', '--force-reinstall' ]) self.message( 'The software was updated and this session will be closed. Please execute the software again.', 'Restart required') exit() break else: print('Enabled to check new versions') except Exception as e: print('Enabled to check new versions:')
class BaseModule(BaseWidget): """Application form""" def __init__(self): global conf conf += 'pythonvideoannotator.resources' # Resources can only be loaded after pyqt is running super(BaseModule, self).__init__('Video annotation editor') self._project = Project(parent=self) Dialog.project = self._project self._player = ControlPlayer("Player") self._time = ControlEventTimeline('Time') self._dock = ControlDockWidget("Timeline", side='bottom', order=1, margin=5) self.formset = ['_player'] self._dock.value = self._time self._player.process_frame_event = self.process_frame_event self._player.click_event = self.on_player_click_event self._time.key_release_event = self.__timeline_key_release_event self.load_order = [] self.mainmenu.insert( 0, { 'File': [{ 'Open': self.__open_project_event, 'icon': conf.ANNOTATOR_ICON_OPEN }, '-', { 'Save': self.__save_project_event, 'icon': conf.ANNOTATOR_ICON_SAVE }, { 'Save as': self.__save_project_as_event, 'icon': conf.ANNOTATOR_ICON_SAVE }, '-', { 'Exit': QApplication.closeAllWindows, 'icon': conf.ANNOTATOR_ICON_EXIT }] }) self.mainmenu.insert(1, {'Modules': []}) self.mainmenu.insert(2, {'Windows': []}) track_user_stats() ######################################################################## ###### CHECK NEW VERSIONS RELEASES ##################################### ######################################################################## try: versions = pypi_xmlrpc.package_releases('Python-video-annotator') if versions is not None: new_version = versions[0] new_version_numbers = [int(x) for x in new_version.split('.')] version_numbers = [int(x) for x in __version__.split('.')] for new_n, n in zip(new_version_numbers, version_numbers): if new_n > n: response = self.question( "<h2>New version <b>[{0}]</b> available</h2>" "<p>Do you wish to update the software?</p>" "<p>The software can be updated later by running the next command in the terminal:</p>" "<i>pip install python-video-annotator --force-reinstall</i>" .format(new_version), 'New version [{0}]'.format(new_version)) if response == 'yes': subprocess.call([ sys.executable, "-m", "pip", "install", 'python-video-annotator', '--force-reinstall' ]) self.message( 'The software was updated and this session will be closed. Please execute the software again.', 'Restart required') exit() break else: print('Enabled to check new versions') except Exception as e: print('Enabled to check new versions:') ###################################################################################### #### FUNCTIONS ####################################################################### ###################################################################################### def init_form(self): super(BaseModule, self).init_form() if conf.CHART_FILE_PATH: self._time.import_chart(*conf.CHART_FILE_PATH) if conf.PROJECT_PATH: self.load_project(conf.PROJECT_PATH) ###################################################################################### #### IO FUNCTIONS #################################################################### ###################################################################################### def save(self, data, project_path=None): self._project.save(data, project_path) return data def load(self, data, project_path=None): try: self._project.load(data, project_path) except FileNotFoundError as e: QMessageBox.critical(self, "Error", str(e)) def save_project(self, project_path=None): try: if project_path is None: project_path = QFileDialog.getExistingDirectory( self, "Select the project directory") if project_path is not None and str(project_path) != '': project_path = str(project_path) self.save({}, project_path) except Exception as e: QMessageBox.critical(self, "Error", str(e)) def load_project(self, project_path=None): if project_path is None: project_path = QFileDialog.getExistingDirectory( self, "Select the project directory") if project_path is not None and str(project_path) != '': self.load({}, str(project_path)) ###################################################################################### #### EVENTS ########################################################################## ###################################################################################### def on_player_click_event(self, event, x, y): """ Code to select a blob with the mouse """ super(VideoAnnotationEditor, self).on_player_click_event(event, x, y) self._player.refresh() def process_frame_event(self, frame): """ Function called before render each frame """ return frame def add_dataset_event(self, dataset): pass def removed_dataset_event(self, dataset): pass def removed_object_event(self, obj): pass def __open_project_event(self): self.load_project() def __save_project_event(self): print('Project saved') self.save_project(self._project.directory) def __save_project_as_event(self): self.save_project() def __timeline_key_release_event(self, event): """ Control video playback using the space bar to Play/Pause """ if event.key() == QtCore.Qt.Key_Space: self._player.stop( ) if self._player.is_playing else _player._video.play() ###################################################################################### #### PROPERTIES ###################################################################### ###################################################################################### @property def timeline(self): return self._time @property def player(self): return self._player @property def video(self): return self._player.value @video.setter def video(self, value): self._player.value = value self._player.enabled = value is not None if value: self._time.max = self._player.max @property def project(self): return self._project
class BaseModule(BaseWidget): """Application form""" def __init__(self): global conf conf += 'pythonvideoannotator.resources' # Resources can only be loaded after pyqt is running super(BaseModule, self).__init__('Video annotation editor') self._project = Project(parent=self) Dialog.project = self._project self._player = ControlPlayer("Player") self._time = ControlEventTimeline('Time') self._dock = ControlDockWidget("Timeline", side='bottom', order=1, margin=5) self._progress = ControlProgress('Progress', visible=False) # define the application toolbar self.toolbar = [ ControlButton('Open', icon=conf.ANNOTATOR_ICON_OPEN, default=self.__open_project_event), ControlButton('Save', icon=conf.ANNOTATOR_ICON_SAVE, default=self.__save_project_event) ] self.formset = ['_player', '_progress'] self._dock.value = self._time self._player.process_frame_event = self.process_frame_event self._player.click_event = self.on_player_click_event self._player.double_click_event = self.on_player_double_click_event self._player.drag_event = self.on_player_drag_event self._player.end_drag_event = self.on_player_end_drag_event self._time.key_release_event = lambda x: x self._player.key_release_event = lambda x: x self.load_order = [] self.mainmenu.insert( 0, { 'File': [{ 'Open': self.__open_project_event, 'icon': conf.ANNOTATOR_ICON_OPEN }, '-', { 'Save': self.__save_project_event, 'icon': conf.ANNOTATOR_ICON_SAVE }, { 'Save as': self.__save_project_as_event, 'icon': conf.ANNOTATOR_ICON_SAVE }, '-', { 'Exit': QApplication.closeAllWindows, 'icon': conf.ANNOTATOR_ICON_EXIT }] }) self.mainmenu.insert(1, {'Modules': []}) self.mainmenu.insert(2, {'Windows': []}) track_user_stats() ######################################################################## ###### CHECK NEW VERSIONS RELEASES ##################################### ######################################################################## try: versions = pypi_xmlrpc.package_releases('Python-video-annotator') if versions is not None: new_version = versions[0] if float(new_version) > float(__version__): response = self.question( "<h2>New version <b>[{0}]</b> available</h2>" "<p>Do you wish to update the software?</p>" "<p>The software can be updated later by running the next command in the terminal:</p>" "<i>pip install python-video-annotator --force-reinstall</i>" .format(new_version), 'New version [{0}]'.format(new_version)) if response == 'yes': subprocess.call([ sys.executable, "-m", "pip", "install", 'python-video-annotator', '--force-reinstall' ]) self.message( 'The software was updated and this session will be closed. Please execute the software again.', 'Restart required') exit() else: print('Unable to check new versions') except Exception as e: print('Unable to check new versions:') ###################################################################################### #### FUNCTIONS ####################################################################### ###################################################################################### def init_form(self): super(BaseModule, self).init_form() if conf.CHART_FILE_PATH: self._time.import_chart(*conf.CHART_FILE_PATH) if conf.VIDEOANNOTATOR_PROJECTPATH: self.load_project(conf.VIDEOANNOTATOR_PROJECTPATH) if len(sys.argv) > 1: QTimer.singleShot(1000, self.__load_project_from_argv) ###################################################################################### #### IO FUNCTIONS #################################################################### ###################################################################################### def save(self, data, project_path=None): self._project.save(data, project_path) return data def load(self, data, project_path=None): try: self._project.load(data, project_path) except FileNotFoundError as e: QMessageBox.critical(self, "Error", str(e)) def save_project(self, project_path=None): try: if project_path is None: project_path = QFileDialog.getExistingDirectory( self, "Select the project directory") if project_path is not None and str(project_path) != '': project_path = str(project_path) self.save({}, project_path) except Exception as e: traceback.print_exc() QMessageBox.critical(self, "Error", str(e)) def load_project(self, project_path=None): if project_path is None: project_path = QFileDialog.getExistingDirectory( self, "Select the project directory") if project_path is not None and str(project_path) != '': self.load({}, str(project_path)) ###################################################################################### #### EVENTS ########################################################################## ###################################################################################### def on_player_drag_event(self, p1, p2): if self._project: self._project.player_on_drag(p1, p2) self._player.refresh() def on_player_end_drag_event(self, p1, p2): if self._project: self._project.player_on_end_drag(p1, p2) self._player.refresh() def on_player_click_event(self, event, x, y): """ Code to select a blob with the mouse """ if self._project: self._project.player_on_click(event, x, y) self._player.refresh() def on_player_double_click_event(self, event, x, y): """ Code to select a blob with the mouse """ if self._project: self._project.player_on_double_click(event, x, y) self._player.refresh() def process_frame_event(self, frame): """ Function called before render each frame """ return frame def add_dataset_event(self, dataset): pass def removed_dataset_event(self, dataset): pass def removed_object_event(self, obj): pass def __open_project_event(self): self.load_project() def __save_project_event(self): self.save_project(self._project.directory) def __save_project_as_event(self): self.save_project() def keyReleaseEvent(self, event): ###################################################################################### #### SPECIFIC SHORTCUTS############################################################### ###################################################################################### #Go to the next event and then click the mark the point button if event.key() == QtCore.Qt.Key_I: if self.timeline.timeline_widget.selected is not None and self.timeline.timeline_widget.selected != self.timeline.timeline_widget.pointer: self.move_to_next_event() self.mark_point() #Select the path of the next object and click the mark the point button if event.key() == QtCore.Qt.Key_U: self.select_next_path() self.mark_point() #"Click" the Mark Point button in the current Path elif event.key() == QtCore.Qt.Key_O: self.mark_point() ###################################################################################### #### TIMELINE SHORTCUTS ############################################################## ###################################################################################### if self.timeline.timeline_widget.selected is not None: modifier = int(event.modifiers()) if modifier == QtCore.Qt.ControlModifier and event.key( ) == QtCore.Qt.Key_Left: self.move_event_or_pointer_left(event) if modifier == QtCore.Qt.ControlModifier and event.key( ) == QtCore.Qt.Key_Right: self.move_event_or_pointer_right(event) if self.timeline.timeline_widget.selected != self.timeline.timeline_widget.pointer: # Delete the selected event if event.key() == QtCore.Qt.Key_Delete: self.timeline.timeline_widget.remove_selected_event() # Lock or unlock an event if event.key() == QtCore.Qt.Key_L: self.timeline.timeline_widget.toggle_selected_event_lock() if event.key() == QtCore.Qt.Key_E: self.move_to_next_event() if event.key() == QtCore.Qt.Key_Q: self.move_to_previous_event() if modifier == QtCore.Qt.ControlModifier and event.key( ) == QtCore.Qt.Key_Up: self.move_event_up() if modifier == QtCore.Qt.ControlModifier and event.key( ) == QtCore.Qt.Key_Down: self.move_event_down() if modifier == int(QtCore.Qt.ShiftModifier | QtCore.Qt.ControlModifier) and event.key( ) == QtCore.Qt.Key_Left: self.move_event_end_left() if modifier == int(QtCore.Qt.ShiftModifier | QtCore.Qt.ControlModifier) and event.key( ) == QtCore.Qt.Key_Right: self.move_event_end_right() if modifier == QtCore.Qt.ShiftModifier and event.key( ) == QtCore.Qt.Key_Left: self.move_event_begin_left() if modifier == QtCore.Qt.ShiftModifier and event.key( ) == QtCore.Qt.Key_Right: self.move_event_begin_right() else: if event.key() == QtCore.Qt.Key_S: self.create_event_at_current_frame() # walk backwards 1 step elif event.key() == QtCore.Qt.Key_A: self.timeline.timeline_widget.position = self.timeline.timeline_widget.position - 1 # forward 1 step elif event.key() == QtCore.Qt.Key_D: self.timeline.timeline_widget.position = self.timeline.timeline_widget.position + 1 elif event.key() == QtCore.Qt.Key_E: self.move_to_first_event() elif event.key() == QtCore.Qt.Key_Q: self.move_to_last_event() ###################################################################################### #### PLAYER SHORTCUTS ################################################################ ###################################################################################### # Control video playback using the space bar to Play/Pause if event.key() == QtCore.Qt.Key_Space: if self.player.is_playing: self.player.stop() else: self.player.play() # Jumps 1 frame backwards elif event.key() == QtCore.Qt.Key_A: self.player.video_widget.control.video_index -= 2 self.player.video_widget.control.call_next_frame() # Jumps 1 frame forward elif event.key() == QtCore.Qt.Key_D: self.player.video_widget.control.call_next_frame() # Jumps 20 seconds backwards elif event.key() == QtCore.Qt.Key_Z: self.player.video_widget.control.jump_backward() self.player.video_widget.control.call_next_frame() # Jumps 20 seconds forward elif event.key() == QtCore.Qt.Key_C: self.player.video_widget.control.jump_forward() self.player.video_widget.control.call_next_frame() elif event.key() == QtCore.Qt.Key_M: self._move_img = False elif event.key() == QtCore.Qt.Key_1: self.player.video_widget.control.next_frame_step = 1 self.player.video_widget.show_tmp_msg('Speed: 1x') elif event.key() == QtCore.Qt.Key_2: self.player.video_widget.control.next_frame_step = 2 self.player.video_widget.show_tmp_msg('Speed: 2x') elif event.key() == QtCore.Qt.Key_3: self.player.video_widget.control.next_frame_step = 3 self.player.video_widget.show_tmp_msg('Speed: 3x') elif event.key() == QtCore.Qt.Key_4: self.player.video_widget.control.next_frame_step = 4 self.player.video_widget.show_tmp_msg('Speed: 4x') elif event.key() == QtCore.Qt.Key_5: self.player.video_widget.control.next_frame_step = 5 self.player.video_widget.show_tmp_msg('Speed: 5x') elif event.key() == QtCore.Qt.Key_6: self.player.video_widget.control.next_frame_step = 6 self.player.video_widget.show_tmp_msg('Speed: 6x') elif event.key() == QtCore.Qt.Key_7: self.player.video_widget.control.next_frame_step = 7 self.player.video_widget.show_tmp_msg('Speed: 7x') elif event.key() == QtCore.Qt.Key_8: self.player.video_widget.control.next_frame_step = 8 self.player.video_widget.show_tmp_msg('Speed: 8x') elif event.key() == QtCore.Qt.Key_9: self.player.video_widget.control.next_frame_step = 9 self.player.video_widget.show_tmp_msg('Speed: 9x') def __load_project_from_argv(self): self.load_project(sys.argv[-1]) ###################################################################################### #### EVENT FUNCTIONS ################################################################# ###################################################################################### def select_next_path(self): selected = self.project.tree.selected_item if selected is not None: #If it's a video, try to select its first object and the object's first child if isinstance(selected.win, Video): if selected.childCount() > 0: child_object = selected.child(0) if child_object.childCount() > 0: self.project.tree.selected_item = child_object.child(0) #If it's an object, try to select it's first child elif isinstance(selected.win, Object2D): if selected.childCount() > 0: self.project.tree.selected_item = selected.child(0) #If it's a path try to select the first child of the next object of their parent video elif isinstance(selected.win, Path): parent_object = selected.parent() parent_video = parent_object.parent() parent_object_index = parent_video.indexOfChild(parent_object) if parent_object_index < parent_video.childCount() - 1: next_object = parent_video.child( parent_video.indexOfChild(parent_object) + 1) if next_object.childCount() > 0: self.project.tree.selected_item = next_object.child(0) #If it's the last object of the video, go back to the path of the first one else: next_object = parent_video.child(0) if next_object.childCount() > 0: self.project.tree.selected_item = next_object.child(0) def mark_point(self): selected = self.project.tree.selected_item if selected is not None and isinstance(selected.win, Path): path = selected.win if not path.mark_point_button.checked: path.mark_point_button.click() def move_to_next_event(self): index = self.timeline.timeline_widget.selected_row.events.index( self.timeline.timeline_widget.selected) if index < len(self.timeline.timeline_widget.selected_row.events) - 1: self.timeline.timeline_widget.selected = self.timeline.timeline_widget.selected_row.events[ index + 1] self.timeline.timeline_widget.position = self.timeline.timeline_widget.selected.begin def move_to_previous_event(self): index = self.timeline.timeline_widget.selected_row.events.index( self.timeline.timeline_widget.selected) if index > 0: self.timeline.timeline_widget.selected = self.timeline.timeline_widget.selected_row.events[ index - 1] self.timeline.timeline_widget.position = self.timeline.timeline_widget.selected.begin def move_to_first_event(self): if self.timeline.timeline_widget.selected_row is not None and len( self.timeline.timeline_widget.selected_row) > 0: self.timeline.timeline_widget.selected = self.timeline.timeline_widget.selected_row.events[ 0] self.timeline.timeline_widget.position = self.timeline.timeline_widget.selected.begin def move_to_last_event(self): if self.timeline.timeline_widget.selected_row is not None and len( self.timeline.timeline_widget.selected_row) > 0: self.timeline.timeline_widget.selected = self.timeline.timeline_widget.selected_row.events[ len(self.timeline.timeline_widget.selected_row) - 1] self.timeline.timeline_widget.position = self.timeline.timeline_widget.selected.begin def move_event_or_pointer_left(self, event): modifier = int(event.modifiers()) self.timeline.timeline_widget.selected.move(-1, 0) event.ignore() self.timeline.timeline_widget.repaint() def move_event_or_pointer_right(self, event): modifier = int(event.modifiers()) self.timeline.timeline_widget.selected.move(1, 0) event.ignore() self.timeline.timeline_widget.repaint() def move_event_up(self): self.timeline.timeline_widget.selected.move( 0, self.timeline.timeline_widget.selected.top_coordinate - self.timeline.timeline_widget.TRACK_HEIGHT) self.timeline.timeline_widget.repaint() def move_event_down(self): self.timeline.timeline_widget.selected.move( 0, self.timeline.timeline_widget.selected.top_coordinate + self.timeline.timeline_widget.TRACK_HEIGHT) self.timeline.timeline_widget.repaint() def move_event_end_left(self): self.timeline.timeline_widget.selected.move_end(-1) self.timeline.timeline_widget.repaint() def move_event_end_right(self): self.timeline.timeline_widget.selected.move_end(1) self.timeline.timeline_widget.repaint() def move_event_begin_left(self): self.timeline.timeline_widget.selected.move_begin(-1) self.timeline.timeline_widget.repaint() def move_event_begin_right(self): self.timeline.timeline_widget.selected.move_begin(1) self.timeline.timeline_widget.repaint() def create_event_at_current_frame(self): if not self.timeline.timeline_widget.creating_event: # Start self.timeline.timeline_widget.creating_event_start = self.timeline.timeline_widget.pointer.frame self.timeline.timeline_widget.creating_event = True # TODO Add some indicator that an event is being recorded, like # using the track selector circle to become red else: # End, must be followed right after Start key and have no # effect otherwise self.timeline.timeline_widget.creating_event_end = self.timeline.timeline_widget.pointer.frame start = self.timeline.timeline_widget.creating_event_start end = self.timeline.timeline_widget.creating_event_end comment = "" if end > start: track = self.timeline.timeline_widget.selected_row if track is None and len( self.timeline.timeline_widget.tracks) > 0: track = self.timeline.timeline_widget.tracks[0] if track is None: track = self.timeline.timeline_widget.add_track() self.timeline.timeline_widget.add_event(start, end, comment, track=track) self.timeline.timeline_widget.repaint() self.timeline.timeline_widget.creating_event = False else: self.timeline.timeline_widget.creating_event = False ###################################################################################### ###################################################################################### #### PROPERTIES ###################################################################### ###################################################################################### @property def progress_bar(self): return self._progress @property def timeline(self): return self._time @property def player(self): return self._player @property def video(self): return self._player.value @video.setter def video(self, value): self._player.value = value self._player.enabled = value is not None if value: self._time.max = self._player.max @property def project(self): return self._project
def __init__(self, *args, **kwargs): super().__init__('Hazard Labelling') self._args = { "filepath": FILEPATH, "folder": FOLDER, "dest": DEST } self.set_margin(10) #Definition of the forms fields self._videofile = ControlFile('Video') self._hazardbutton = ControlButton('Hazard') self._next = ControlButton('Next Video') self._save_data = ControlButton('Save labels') self._player = ControlPlayer('Player') self._timeline = ControlEventTimeline('Timeline') self._panel = ControlDockWidget(label='Timeline', side='bottom', margin=10) self._status = ControlText('Status') self._file_list = [] if self._args["folder"] is None: if self._args["filepath"] is not None: self._file_list = self._args["filepath"] elif self._args["folder"] is not None: if os.path.isdir(self._args["folder"]): self.__updateStatus("Source folder found at: {}".format(self._args["folder"])) print("Scanning folder and all subfolders... {}".format(self._args["folder"])) count = 0 for (dirpath, dirnames, filenames) in os.walk(self._args["folder"]): path = [] for f in filenames: if f.rsplit('.')[-1] in ACCEPTABLE_EXT: count += 1 path.append(dirpath + "/" + f) self._file_list.extend(path) if count % 100 == 0: print("Found {} files...".format(count)) print("Scan complete, found {} acceptable files".format(count)) self._video_count = len(self._file_list) self._progress = ControlProgress(label="Video %p of " + str(self._video_count), defaultValue=1, min=1, max=self._video_count) self._hazard_counter = 0 self._current_video = 0 #Define function calls on button presses self._videofile.changed_event = self.__videoFileSelectionEvent self._hazardbutton.value = self.__labelHazard self._next.value = self.__nextVideo self._save_data.value = self.__saveData self._panel.value = self._timeline self._progress.value = self._current_video + 1 #Define events self._player.process_frame_event = self.__processFrame self._player.click_event = self.__clickEvent self._player.key_release_event = self.__tagEvent #Define the organization of the Form Controls self._formset = [ '_player', # '_hazardbutton', '_panel', ('_videofile', '_next'), ('_status', '_save_data'), '_progress' ] self._video_loaded = False try: self.__videoFileSelect(self._file_list[self._current_video]) except Exception as e: self.__updateStatus("Select video...") self._hazard_default_duration = 0
class VideoWindow(BaseWidget): def __init__(self, *args, **kwargs): super().__init__('Hazard Labelling') self._args = { "filepath": FILEPATH, "folder": FOLDER, "dest": DEST } self.set_margin(10) #Definition of the forms fields self._videofile = ControlFile('Video') self._hazardbutton = ControlButton('Hazard') self._next = ControlButton('Next Video') self._save_data = ControlButton('Save labels') self._player = ControlPlayer('Player') self._timeline = ControlEventTimeline('Timeline') self._panel = ControlDockWidget(label='Timeline', side='bottom', margin=10) self._status = ControlText('Status') self._file_list = [] if self._args["folder"] is None: if self._args["filepath"] is not None: self._file_list = self._args["filepath"] elif self._args["folder"] is not None: if os.path.isdir(self._args["folder"]): self.__updateStatus("Source folder found at: {}".format(self._args["folder"])) print("Scanning folder and all subfolders... {}".format(self._args["folder"])) count = 0 for (dirpath, dirnames, filenames) in os.walk(self._args["folder"]): path = [] for f in filenames: if f.rsplit('.')[-1] in ACCEPTABLE_EXT: count += 1 path.append(dirpath + "/" + f) self._file_list.extend(path) if count % 100 == 0: print("Found {} files...".format(count)) print("Scan complete, found {} acceptable files".format(count)) self._video_count = len(self._file_list) self._progress = ControlProgress(label="Video %p of " + str(self._video_count), defaultValue=1, min=1, max=self._video_count) self._hazard_counter = 0 self._current_video = 0 #Define function calls on button presses self._videofile.changed_event = self.__videoFileSelectionEvent self._hazardbutton.value = self.__labelHazard self._next.value = self.__nextVideo self._save_data.value = self.__saveData self._panel.value = self._timeline self._progress.value = self._current_video + 1 #Define events self._player.process_frame_event = self.__processFrame self._player.click_event = self.__clickEvent self._player.key_release_event = self.__tagEvent #Define the organization of the Form Controls self._formset = [ '_player', # '_hazardbutton', '_panel', ('_videofile', '_next'), ('_status', '_save_data'), '_progress' ] self._video_loaded = False try: self.__videoFileSelect(self._file_list[self._current_video]) except Exception as e: self.__updateStatus("Select video...") self._hazard_default_duration = 0 def __videoFileSelect(self, filepath): try: self._videofile.value = str(filepath) self._player.value = self._videofile.value self._player.refresh() self._player.update_frame() # Hazard set to occur for 60 frames upon flagging self._hazard_default_duration = int(self._player.fps * 2) self._video_loaded = True except Exception as e: self.__updateStatus("Unable to select video") def __videoFileSelectionEvent(self): """ When the videofile is selected instantiate the video in the player """ try: self._player.value = self._videofile.value # Hazard set to occur for 60 frames upon flagging self._hazard_default_duration = int(self._player.fps * 2) self._video_loaded = True except Exception as e: self.__updateStatus("No video selected") def __nextVideo(self): if self._current_video >= (self._video_count - 1): self.__updateStatus("No more videos") else: self._current_video += 1 self.__reset() self.__videoFileSelect(self._file_list[self._current_video]) self._progress.value = self._current_video + 1 def __reset(self): self._player.stop() self.__saveData() print("saving on reset") self._timeline.clean() self.__updateStatus("") def __processFrame(self, frame): """ Do some processing to the frame and return the result frame """ return frame def __clickEvent(self, click_event, x, y): self.__labelHazard() def __tagEvent(self, event): """ Label hazard using Enter key """ key = event.key() # QtCore.Qt.Key_Enter gives wrong value (at least on test PC) for Enter key # Desired == 16777221, actual == 16777220 key_id = 16777220 if event.key() == key_id: self.__labelHazard() def __addFlag(self, value): self._timeline.add_period(value) def __labelHazard(self): if self._video_loaded: if self._player.video_index > (self._player.max - self._hazard_default_duration): flag_duration = round(self._player.max - self._player.video_index) else: flag_duration = self._hazard_default_duration try: self._hazard_counter += 1 self.__updateStatus("Hazard flagged! | Frame: {} Timestamp: {}".format(self._player.video_index, round(self._player.video_index/self._player.fps, 3))) self.__addFlag((self._player.video_index, self._player.video_index + flag_duration, str(self._hazard_counter))) except Exception as e: try: self._player.refresh() self.__updateStatus("Hazard flagged! | Frame: {} Timestamp: {}".format(self._player.video_index, round(self._player.video_index/self._player.fps, 3))) except Exception as e: self.__updateStatus("Unable to label, exiting...") sys.exit(0) def __saveData(self): self._timeline.export_csv_file(self._videofile.value + "_hazard.csv") self.__updateStatus("Saving {} to {}".format(self._videofile.value + "_hazard.csv", self._args["dest"])) def __updateStatus(self, msg): self._status.value = str(msg)