Beispiel #1
0
    def estimate(self, roi: Roi = None) -> None:
        """Estimate the transform matrix from a set of coordinates.
            Coordinates should correspond to the corners of the outline of
            the design, relative to the video frame size:
                x in [0,1] ~ width
                y in [0,1] ~ height
        """
        # todo: sanity check roi

        if roi is not None:
            self.config(roi=roi)

            self.set(self._implementation.estimate(self.adjust(roi), self._video_shape, self._design_shape))
            streams.update()
Beispiel #2
0
    def clear_filters(self) -> bool:
        log.debug(f"clearing filters")

        for mask in self.masks:
            mask.clear_filter()

        self.set_state(AnalyzerState.CAN_FILTER)
        self.state_transition()
        self.event(PushEvent.CONFIG, self.get_config())
        self.commit()

        streams.update()

        return True
Beispiel #3
0
    def test_normal_operation(self):
        class TestRegistry(ImmutableRegistry):
            method1 = Endpoint(Callable[[], np.ndarray], stream_image)
            method2 = Endpoint(Callable[[], np.ndarray], stream_image)

        test_registry = TestRegistry()

        class StreamableClass(Instance, RootInstance):
            _endpoints = test_registry
            _instance_class = Instance
            _config_class = BaseConfig

            def __init__(self):
                super().__init__()
                self._gather_instances()

            @stream
            @test_registry.expose(test_registry.method1)
            def method1(self) -> np.ndarray:
                return np.random.randint(0, 255, (64, 64, 3), dtype=np.uint8)

            @stream
            @test_registry.expose(test_registry.method2)
            def method2(self) -> np.ndarray:
                return np.random.randint(0, 255, (64, 64, 3), dtype=np.uint8)

        streamable = StreamableClass()

        thread1 = StreamerThread(
            streams.register(streamable,
                             streamable.get(test_registry.method1)).stream())
        thread1.start()
        thread2 = StreamerThread(
            streams.register(streamable,
                             streamable.get(test_registry.method2)).stream())
        thread2.start()

        streams.update()
        time.sleep(0.1)

        # Unregiter specific method
        streams.unregister(streamable, streamable.get(test_registry.method1))
        # Unregister all methods & stop
        streams.stop()

        # FrameStreamer -> double yield
        # each streamer pushes once when registered and once on streams.update()
        self.assertEqual(4, len(thread1.data))
        self.assertEqual(4, len(thread2.data))
Beispiel #4
0
    def seek(self, position: float = None) -> float:
        """Seek to the relative position ~ [0,1]
        """
        # todo: had to remove lock to enable reading frames :/
        #       (otherwise streams.update() can get deadocked @ VideoFileHandler if not reading frames from cache)
        if position is not None:
            frame_number = int(position * self.frame_count)
            if settings.cache.resolve_frame_number:
                self.frame_number = self._resolve_frame(frame_number)
            else:
                self.frame_number = frame_number

        log.debug(f"seeking  {self.path} {self.frame_number}/{self.frame_count}")
        streams.update()

        return self.frame_number / self.frame_count
Beispiel #5
0
    def set_filter_click(self, relative_x: float, relative_y: float):
        log.debug(f'set_filter_click @ ({relative_x}, {relative_y})')

        click = ShapeCoo(
            x = relative_x,
            y = relative_y,
            shape = self.design.shape[::-1]
        )

        hits = [mask for mask in self.masks if mask.contains(click)]

        if len(hits) == 1:
            hit = hits[0]
            frame = self.video.read_frame()
            click = self.transform.coordinate(click)

            color = HsvColor(*click.value(frame))
            log.debug(f"color @ {click.idx}: {color}")

            hit.set_filter(color)

            for fs in self._featuresets.values():
                for feature in fs.features:
                    assert isinstance(feature, MaskFunction)
                    try:
                        if feature.mask == hit:  # type: ignore
                            feature._ready = True
                    except AttributeError:
                        pass

            self.get_colors()

            self.state_transition()
            self.event(PushEvent.CONFIG, self.get_config())
            self.commit()

            streams.update()
            self.commit()
        elif len(hits) == 0:
            log.debug(f"no hit for {click.idx}")
        elif len(hits) > 1:
            self.notice(
                f"Multiple valid options: {[hit.name for hit in hits]}. "
                f"Select a point where masks don't overlap."
            )
Beispiel #6
0
    def set_config(self, config: dict, silent: bool = False) -> dict:
        with self.lock():
            if True:  # todo: sanity check
                do_commit = False
                do_relaunch = False
                log.debug(f"Setting VideoAnalyzerConfig to {config}")

                previous_name = copy.copy(self.config.name)
                previous_desc = copy.copy(self.config.description)
                previous_Nf = copy.copy(self.config.Nf)
                previous_dt = copy.copy(self.config.dt)
                previous_fis = copy.copy(self.config.frame_interval_setting)
                previous_features = copy.deepcopy(self.config.features)  # todo: clean up
                previous_video_path = copy.deepcopy(self.config.video_path)
                previous_design_path = copy.deepcopy(self.config.design_path)
                previous_design = copy.deepcopy(self.config.design)
                previous_flip = copy.deepcopy(self.config.transform.flip)
                previous_turn = copy.deepcopy(self.config.transform.turn)
                previous_roi = copy.deepcopy(self.config.transform.roi)
                previous_masks = copy.deepcopy(self.config.masks)

                # Set implementations
                if hasattr(self, 'transform') and 'transform' in config and 'type' in config['transform']:
                    self.transform.set_implementation(config['transform']['type'])   # todo: shouldn't do this every time
                if hasattr(self, 'design') and 'masks' in config:
                    for i, mask in enumerate(self.design.masks):
                        if 'filter' in config['masks'][i] and 'type' in config['masks'][i]['filter']:
                            mask.filter.set_implementation(config['masks'][i]['filter']['type'])  # todo: shouldn't do this every time

                self._set_config(config)

                if hasattr(self, 'transform'):
                    self.transform._config(**self.config.transform.to_dict())
                    self.transform.set_implementation()   # todo: shouldn't do this every time
                if hasattr(self, 'design'):
                    self.design._config(**self.config.design.to_dict())

                # Check for file changes
                if self.launched and previous_video_path != self.config.video_path:
                    do_commit = True
                    do_relaunch = True

                if self.launched and previous_design_path != self.config.design_path:
                    do_commit = True
                    do_relaunch = True

                # Check for design render changes
                if previous_design != self.config.design:
                    self.design._render(mask_config=self.config.masks)
                    self.transform._design_shape = self.design.shape
                    self.estimate_transform()
                    do_commit = True

                # Check for name/description changes
                if self.config.name != previous_name:
                    do_commit = True
                if self.config.description != previous_desc:
                    do_commit = True

                # Check for changes in frames
                if hasattr(self, 'video'):
                    if any([
                        previous_Nf != self.config.Nf,
                        previous_dt != self.config.dt,
                        previous_fis != self.config.frame_interval_setting
                    ]):
                        self.video.set_requested_frames(list(self.frame_numbers()))
                        do_commit = True

                # Check for changes in features
                if previous_features != self.config.features:
                    if self.launched:
                        self._get_featuresets()

                    do_commit = True

                # Get featureset instances  todo: overlap with previous block?
                if self.launched and not self._featuresets:
                    self._get_featuresets()

                # Check for ROI adjustments
                if previous_flip != self.config.transform.flip \
                        or previous_turn != self.config.transform.turn \
                        or previous_roi != self.config.transform.roi:
                    self.estimate_transform()  # todo: self.config.bla.thing should be exactly self.bla.config.thing always
                    do_commit = True

                # Check for mask adjustments
                if previous_masks != self.config.masks:
                    for i, mask in enumerate(self.design.masks):
                        mask.set_config(self.config.masks[i].to_dict())

                    do_commit = True

                if do_commit and not silent:  # todo: better config handling in AnalysisMdoel.store() instead!
                    self.commit()

                if do_relaunch:
                    self._launch()
                    self._gather_instances()

                # Check for state transitions
                self.state_transition(push=True)

                config = self.get_config()

                # Push config event
                self.event(PushEvent.CONFIG, config)

                # Push streams
                streams.update()

                return config
Beispiel #7
0
    def clear(self) -> None:
        self.config(roi=None)
        self.set(None)

        streams.update()