예제 #1
0
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()
예제 #2
0
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