class MotionCounter(BaseWidget): def __init__(self, parent=None): BaseWidget.__init__(self, 'Motion counter', parent_win=parent) self.set_margin(5) self.setMinimumHeight(300) self.setMinimumWidth(500) self._player = ControlPlayer('Player') self._datasets = ControlEmptyWidget('Paths', default=DatasetsDialog()) self._backgrounds = ControlEmptyWidget('Backgrounds', default=ObjectsDialog()) self._show_diff = ControlCheckBox('Show diffs boxes') self._threshold = ControlSlider('Threshold', default=5, minimum=1, maximum=255) self._radius = ControlSlider('Radius', default=30, minimum=1, maximum=200) self._apply = ControlButton('Apply', checkable=True) self._compare = ControlCombo('Compare with') self._progress = ControlProgress('Progress') self._formset = [ '_datasets', '=', '_compare', '_backgrounds', ('_threshold', '_radius', '_show_diff'), '_player', '_apply', '_progress' ] self._compare.add_item('Last frame', 1) self._compare.add_item('First frame', 2) self._compare.add_item('Background image', 3) self.load_order = ['_threshold', '_radius', '_show_diff'] self._backgrounds.value.datasets_filter = lambda x: isinstance( x, Image) self._datasets.value.datasets_filter = lambda x: isinstance( x, (Contours, Path)) self._player.process_frame_event = self.__process_frame_event self._datasets.value.video_selection_changed_event = self.__video_selection_changed_event self._compare.changed_event = self.__compare_changed_event self._apply.value = self.__apply_btn_event self._apply.icon = conf.ANNOTATOR_ICON_MOTION self._progress.hide() self._backgrounds.hide() ########################################################################### ### EVENTS ################################################################ ########################################################################### def __compare_changed_event(self): if self._compare.value == 1: self._backgrounds.hide() elif self._compare.value == 2: self._backgrounds.hide() elif self._compare.value == 3: self._backgrounds.show() self._lastframe = None def __video_selection_changed_event(self): video = self._datasets.value.selected_video if video is not None: self._player.value = video.video_capture def __process_frame_event(self, frame): index = self._player.video_index - 1 selected_video = self._datasets.value.selected_video radius = self._radius.value threshold = self._threshold.value show_diff = self._show_diff.value compare_with = self._compare.value if compare_with == 3 and len(self._backgrounds.value.objects): background_img = self._backgrounds.value.objects[0].image background_img = cv2.cvtColor(background_img, cv2.COLOR_BGR2GRAY) else: background_img = None for video, (begin, end), datasets in self._datasets.value.selected_data: if video != selected_video: continue for dataset in datasets: pos = dataset.get_position(index) if pos is None: continue if show_diff: # calculate the cut x, y = pos cutx = int(round(x - radius)) cuty = int(round(y - radius)) cutxx = int(round(x + radius)) cutyy = int(round(y + radius)) if cutx < 0: cutx = 0 if cutxx > frame.shape[1]: cutxx = frame.shape[1] if cuty < 0: cuty = 0 if cutyy > frame.shape[0]: cutyy = frame.shape[0] gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) small = gray[cuty:cutyy, cutx:cutxx].copy() circular_mask = np.zeros((cutyy - cuty, cutxx - cutx), dtype=np.uint8) cv2.circle(circular_mask, (cutyy - cuty, cutxx - cutx), radius, 255, -1) small_masked = cv2.bitwise_and(circular_mask, small) if not hasattr(self, '_lastframe') or type( self._lastframe) is not np.ndarray: if self._compare.value == 1 or self._compare.value == 2: self._lastframe = gray elif self._compare.value == 3: self._lastframe = background_img if type(self._lastframe) is np.ndarray: last_masked = cv2.bitwise_and( circular_mask, self._lastframe[cuty:cutyy, cutx:cutxx]) diff = cv2.absdiff(small_masked, last_masked) cv2.circle(frame, pos, radius, (0, 0, 0), -1) frame[cuty:cutyy, cutx:cutxx] += cv2.merge( (diff, diff, diff)) if self._compare.value == 1: self._lastframe = gray else: cv2.circle(frame, pos, radius, (0, 0, 255), 2) return frame def __apply_btn_event(self): if self._apply.checked: self._datasets.enabled = False self._show_diff.enabled = False self._threshold.enabled = False self._player.enabled = False self._radius.enabled = False self._player.stop() self._apply.label = 'Cancel' total_2_analyse = 0 for video, (begin, end), datasets in self._datasets.value.selected_data: total_2_analyse += end - begin + 1 self._progress.min = 0 self._progress.max = total_2_analyse self._progress.show() radius = self._radius.value threshold = self._threshold.value count = 0 for video, (begin, end), datasets in self._datasets.value.selected_data: begin = int(begin) end = int(end) + 1 capture = cv2.VideoCapture(video.filepath) capture.set(cv2.CAP_PROP_POS_FRAMES, begin) last_image = [None for x in datasets] compare_with = self._compare.value if compare_with == 3: if len(self._backgrounds.value.objects): background_img = self._backgrounds.value.objects[ 0].image background_img = cv2.cvtColor(background_img, cv2.COLOR_BGR2GRAY) else: self.critical('No background selected') break else: background_img = None for index in range(begin, end): res, frame = capture.read() if not res: break if not self._apply.checked: break for dataset_index, dataset in enumerate(datasets): pos = dataset.get_position(index) if pos is None: continue x, y = pos cutx = int(round(x - radius)) cuty = int(round(y - radius)) cutxx = int(round(x + radius)) cutyy = int(round(y + radius)) if cutx < 0: cutx = 0 if cutxx > frame.shape[1]: cutxx = frame.shape[1] if cuty < 0: cuty = 0 if cutyy > frame.shape[0]: cutyy = frame.shape[0] gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) small = gray[cuty:cutyy, cutx:cutxx].copy() circular_mask = np.zeros((cutyy - cuty, cutxx - cutx), dtype=np.uint8) cv2.circle(circular_mask, (cutyy - cuty, cutxx - cutx), radius, 255, -1) small_masked = cv2.bitwise_and(small, small, circular_mask) if type(last_image[dataset_index]) is not np.ndarray: last_image[dataset_index] = small_masked if compare_with == 1 or compare_with == 2: last_image[dataset_index] = gray elif compare_with == 3: last_image[dataset_index] = background_img segment = last_image[dataset_index] last_masked = cv2.bitwise_and( circular_mask, segment[cuty:cutyy, cutx:cutxx]) diff = cv2.absdiff(small_masked, last_masked) diff[diff < threshold] = 0 diff[diff >= threshold] = 1 motion = np.sum(diff) dataset.set_motion(index, motion) if compare_with == 1: last_image[dataset_index] = gray self._progress.value = count count += 1 self._datasets.enabled = True self._show_diff.enabled = True self._threshold.enabled = True self._player.enabled = True self._radius.enabled = True self._apply.label = 'Apply' self._apply.checked = False self._progress.hide()
class VideosExporterGui(BaseWidget, VideosExporterPreview, VideosExporterProcess): def __init__(self, parent=None): super(VideosExporterGui, self).__init__('Videos exporter', parent_win=parent) self.set_margin(5) self.setMinimumHeight(400) self.setMinimumWidth(400) self._toolbox = ControlToolBox('Tool') self._panel_area = ControlEmptyWidget('Set the object area', default=DatasetsDialog(self)) self._panel_colors = ControlEmptyWidget('Set the object color', default=DatasetsDialog(self)) self._panel_imgs = ControlEmptyWidget('Set the video background', default=ImagesDialog(self)) #### path panel ################################################ self._panel_path = ControlEmptyWidget('Set the object path', default=DatasetsDialog(self)) self._drawpath = ControlCheckBox('Draw paths') ################################################################ #### draw events ############################################### self._drawevents = ControlCheckBoxList('Events') self._eventstitles = ControlCheckBox('Draw titles') self._evtsreload1 = ControlButton('Reload events') ################################################################ #### split by events ########################################### self._splitevents = ControlCheckBoxList('Events') self._evtsreload2 = ControlButton('Reload events') ################################################################ self._codec = ControlCheckBox('Force AVI') self._outdir = ControlDir('Output directory') self._outfile = ControlText('Output file name') self._player = ControlPlayer('Player') self._progress = ControlProgress('Progress') self._apply = ControlButton('Export video(s)', checkable=True) self._apply.icon = conf.ANNOTATOR_ICON_PATH self._apply.enabled = False self._usefixedsize = ControlCheckBox('Use a fixed size') self._usefixedcolor = ControlCheckBox('Use a fixed color') self._radius = ControlSlider('Circle radius', default=10, minimum=1, maximum=300) self._color = ControlText('BGR color', default='255,255,255') self.formset = [('_toolbox', '||', '_player'), '=', '_outdir', ('_outfile', '_codec'), '_apply', '_progress'] self._toolbox.value = [ ('Path', [self._panel_path, self._drawpath]), ('Circle (optional)', [self._panel_area, (self._usefixedsize, self._radius)]), ('Circle color (optional)', [self._panel_colors, (self._usefixedcolor, self._color)]), ('Background (optional)', [self._panel_imgs]), ('Draw events (optional)', [self._evtsreload1, self._drawevents, self._eventstitles]), ('Split files by events (optional)', [self._evtsreload2, self._splitevents]), ] self._panel_path.value.datasets_filter = lambda x: isinstance( x, (Contours, Path)) #self._panel_area.value.datasets_filter = lambda x: isinstance(x, Value ) self._panel_colors.value.datasets_filter = lambda x: isinstance( x, (Contours, Path)) and hasattr(x, 'has_colors_avg' ) and x.has_colors_avg ### Set the controls events ############################################# self._evtsreload1.value = self.__reload_events self._evtsreload2.value = self.__reload_events self._outfile.changed_event = self.outputfile_changed_event self._usefixedsize.changed_event = self.__usefixedsize_changed_event self._usefixedcolor.changed_event = self.__usefixedcolor_changed_event self._splitevents.selection_changed_event = self.outputfile_changed_event self._panel_path.value.video_selection_changed_event = self.__video_selection_changed_event self._codec.changed_event = self.__video_selection_changed_event ## function from VideosExporterProcess class self._apply.value = self.apply_event ## function from VideosExporterPreview class self._player.process_frame_event = self.player_processframe_event self._evtsreload1.icon = conf.ANNOTATOR_ICON_REFRESH self._evtsreload2.icon = conf.ANNOTATOR_ICON_REFRESH self._progress.hide() self._radius.hide() self._color.hide() self.__check_areatab_event() ########################################################################### ### UTILS ################################################################# ########################################################################### def __reload_events(self): """ Find all the events available on the timeline """ timeline = self.parent().timeline rows = timeline.rows events = {} for row in rows: for event in row.events: events[event.title] = True events = sorted(events.keys()) loaded_events = dict(self._drawevents.items) self._drawevents.value = [(e, loaded_events.get(e, False)) for e in events] loaded_events = dict(self._splitevents.items) self._splitevents.value = [(e, loaded_events.get(e, False)) for e in events] ########################################################################### ### EVENTS ################################################################ ########################################################################### def show(self): """ Load the events when the window is oppened """ super(VideosExporterGui, self).show() self.__reload_events() def __check_areatab_event(self): """ Activate or deactivate the color tab """ if len(list(self._panel_area.value.datasets) ) > 0 or self._usefixedsize.value: self._toolbox.set_item_enabled(2, True) else: self._toolbox.set_item_enabled(2, False) def __usefixedcolor_changed_event(self): if self._usefixedcolor.value: self._color.show() self._panel_colors.hide() else: self._color.hide() self._panel_colors.show() def __usefixedsize_changed_event(self): self.__check_areatab_event() if self._usefixedsize.value: self._radius.show() self._panel_area.hide() else: self._radius.hide() self._panel_area.show() def outputfile_changed_event(self): """ Update the output filename """ filename = self._outfile.value video = self._panel_path.value.selected_video if video is not None: filename = filename if len(filename) > 0 else video.filename videofilepath, video_extension = os.path.splitext(video.filename) outfilepath, outfile_extension = os.path.splitext(filename) names = [outfilepath] if len(outfilepath) > 0 else [] if '{videoindex}' not in outfilepath: names.append('{videoindex}') if len(list(self._splitevents.value)) > 0: if '{event}' not in outfilepath: names.append('{event}') if '{start}' not in outfilepath: names.append('{start}') if '{end}' not in outfilepath: names.append('{end}') self._outfile.value = ('-'.join(names) + video_extension) self._apply.enabled = True else: self._apply.enabled = False def __video_selection_changed_event(self): """ Activate the video preview """ video = self._panel_path.value.selected_video if video is not None: self._player.value = video.video_capture def get_object_area(self, path, areas, index): try: if self._usefixedsize.value: area = (self._radius.value**2 * math.pi) elif len(areas) > 0: a = areas[0] if isinstance(a, Value): area = a.get_value(index) else: area = a.get_area_value(index) else: area = path.get_area_value(index) return area except: return None def get_object_color(self, path, colors, index): try: if self._usefixedcolor.value: color = tuple(eval(self._color.value)) elif len(colors) > 0: color = colors[0].get_color_avg(index) else: color = path.get_color_avg(index) if color is None: raise Exception() except: color = 255, 255, 255 return color