コード例 #1
0
class HsvRangeFilterConfig(FilterConfig):
    """HSV range filter"""
    range: HsvColor = Field(default=HsvColor(h=10, s=75, v=75))
    color: HsvColor = Field(default=HsvColor())
    close: int = Field(default=0, ge=0, le=200)
    open: int = Field(default=0, ge=0, le=200)

    @property
    def ready(self) -> bool:
        return self.color != HsvColor()

    @property
    def c0(self) -> HsvColor:
        return self.color - self.range

    @property
    def c1(self) -> HsvColor:
        return self.color + self.range

    _resolve_close = validator('close', allow_reuse=True)(BaseConfig._odd_add)
    _resolve_open = validator('open', allow_reuse=True)(BaseConfig._odd_add)
    _close_limits = validator('close', pre=True,
                              allow_reuse=True)(BaseConfig._int_limits)
    _open_limits = validator('open', pre=True,
                             allow_reuse=True)(BaseConfig._int_limits)
コード例 #2
0
ファイル: BackgroundFilter.py プロジェクト: ybnd/isimple
class BackgroundFilterConfig(FilterConfig):
    """HSV range filter"""
    range: HsvColor = Field(default=HsvColor(h=10, s=75, v=75))
    color: HsvColor = Field(default=HsvColor())

    @property
    def ready(self) -> bool:
        return self.color != HsvColor()

    @property
    def c0(self) -> HsvColor:
        return self.color - self.range

    @property
    def c1(self) -> HsvColor:
        return self.color + self.range
コード例 #3
0
ファイル: video.py プロジェクト: ybnd/isimple
    def set(self, color: HsvColor = None) -> FilterConfig:
        # if isinstance(self.config.data, dict):  todo: should be ok
        #     self.config.data = self.implementation.config_class()(**self.config.data)

        if color is None:
            color = HsvColor(0,0,0)

        self.config(data=self.implementation.set_filter(self.config.data, color))

        log.debug(f"Filter config: {self.config}")

        return self.config.data
コード例 #4
0
    def __init__(self,
                 elements: Tuple[Instance, ...],
                 global_config: FeatureConfig,
                 config: Optional[dict] = None):
        self._skip = False
        self._ready = False

        self._elements = elements
        self._global_config = global_config

        if config is not None:
            self._config = global_config.__class__(**config)
        else:
            self._config = None

        self._color = HsvColor(h=0, s=200, v=255)  # start out as red
コード例 #5
0
ファイル: video.py プロジェクト: ybnd/isimple
    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."
            )
コード例 #6
0
    def resolve_colors(self) -> Tuple[Color, ...]:
        guideline_colors = [
            as_hsv(f._guideline_color()) for f in self._features
        ]

        min_v = 20.0
        max_v = 255.0
        tolerance = 15

        bins: list = []
        # todo: clean up binning
        for index, color in enumerate(guideline_colors):
            if not bins:
                bins.append([index])
            else:
                in_bin = False
                for bin in bins:
                    if abs(
                            float(color.h) -
                            np.mean([guideline_colors[i].h
                                     for i in bin])) < tolerance:
                        bin.append(index)
                        in_bin = True
                        break
                if not in_bin:
                    bins.append([index])

        for bin in bins:
            if len(bin) < 4:
                increment = 60.0
            else:
                increment = (max_v - min_v) / len(bin)

            for repetition, index in enumerate(bin):
                self._features[index].set_color(
                    HsvColor(h=guideline_colors[index].h,
                             s=220,
                             v=int(max_v - repetition * increment)))

        self._colors = tuple([feature.color for feature in self._features])
        return self.colors
コード例 #7
0
def normalize_config(d: dict) -> dict:
    """Normalize a configuration dictionary to match the current version of `isimple.core.config`
    :param d: configuration dictionary
    :return:
    """

    # If empty dict, return empty dict
    if len(d) == 0:
        return d

    # Deal with legacy formatting
    if VERSION not in d or CLASS not in d:
        if 'version' in d:
            # Support pre-0.3 config metadata field
            d[VERSION] = d.pop('version')
            d[CLASS] = VideoAnalyzerConfig.__name__
        else:
            raise ValueError(f"No version or class info in config")

    def normalizing_to(version):
        log.debug(
            f"Normalizing configuration (from v{d[VERSION]} to v{version})")

    # VideoAnalyzerConfig is the only class that should be deserialized!
    #    -> other classes are contained within it or should only be used
    #       internally. Otherwise, this function would have to be a lot
    #       more complex.  todo: actually, no.
    #                            Just add some nested functions for the
    #                            internal fields, switch ~ class.
    #                            Some classes can use multiple functions;
    #                            go over the fields and pick out functions.
    if d[CLASS] == VideoAnalyzerConfig.__name__:
        if before_version(d[VERSION], '0.2.1'):
            normalizing_to('0.2.1')
            # Rename mask[i].filter.filter to mask[i].filter.data
            for m in d['masks']:
                m['filter']['data'] = m['filter'].pop('filter')
        if before_version(d[VERSION], '0.2.2'):
            normalizing_to('0.2.2')
            # Convert tuple string color '(0,0,0)' to HsvColor string 'HsvColor(h=0, s=0, v=0)'
            from ast import literal_eval as make_tuple  # todo: this is unsafe!
            for m in d['masks']:
                if 'c0' in m['filter']['data']:
                    m['filter']['data']['c0'] = HsvColor(
                        *make_tuple(m['filter']['data']['c0']))
                if 'c1' in m['filter']['data']:
                    m['filter']['data']['c1'] = HsvColor(
                        *make_tuple(m['filter']['data']['c1']))
                if 'radius' in m['filter']['data']:
                    m['filter']['data']['radius'] = HsvColor(
                        *make_tuple(m['filter']['data']['radius']))
        if before_version(d[VERSION], '0.3.1'):
            normalizing_to('0.3.1')
            # Rename TransformHandlerConfig 'coordinates' to 'roi'
            if 'transform' in d:
                if 'coordinates' in d['transform']:
                    d['transform']['roi'] = d['transform'].pop('coordinates')
        if before_version(d[VERSION], '0.3.2'):
            normalizing_to('0.3.2')
            # Remove some fields that should be in the global settings
            to_remove = ('cache_dir', 'cache_size_limit', 'render_dir')
            if 'video' in d:
                for k in to_remove:
                    d['video'].pop(k, None)
            if 'design' in d:
                for k in to_remove:
                    d['design'].pop(k, None)
        if before_version(d[VERSION], '0.3.4'):
            normalizing_to('0.3.4')
            # Set frame_interval_setting to dt, the previous default
            if 'dt' in d:
                d['frame_initerval_setting'] = FrameIntervalSetting('dt')
        if before_version(d[VERSION], '0.3.5'):
            normalizing_to('0.3.5')
            # Convert roi from list to dict
            if 'transform' in d:
                if 'roi' in d['transform']:
                    d['transform']['roi'] = {
                        corner: {
                            'x': coordinate[0],
                            'y': coordinate[1]
                        }
                        for coordinate, corner in zip(
                            json.loads(d['transform']['roi']),
                            ['BL', 'TL', 'TR', 'BR'])
                    }
        if before_version(d[VERSION], '0.3.6'):
            normalizing_to('0.3.6')
            # add ready & skip tags to mask
            if 'masks' in d:
                for m in d['masks']:
                    m['skip'] = False
                    try:
                        m['ready'] = m['filter']['data']['c0'] != HsvColor()
                    except KeyError:
                        m['ready'] = False
        if before_version(d[VERSION], '0.3.7'):
            normalizing_to('0.3.7')
            # remove DesignFileHandlerConfig.keep_renders & VideoFileHandlerConfig.do_resolve_frame_number
            if 'video' in d:
                if 'do_resolve_frame_number' in d['video']:
                    d['video'].pop('do_resolve_frame_number')
            if 'design' in d:
                if 'keep_renders' in d['design']:
                    d['design'].pop('keep_renders')
        if before_version(d[VERSION], '0.3.8'):
            normalizing_to('0.3.8')
            # remove CachingInstance.cache_consumer
            if 'video' in d:
                if 'cache_consumer' in d['video']:
                    d['video'].pop('cache_consumer')
            if 'design' in d:
                if 'cache_consumer' in d['design']:
                    d['design'].pop('cache_consumer')
        if before_version(d[VERSION], '0.3.9'):
            normalizing_to('0.3.9')
            # remove mask.ready attribute
            if 'masks' in d:
                for mask in d['masks']:
                    if 'ready' in mask:
                        mask.pop('ready')
        if before_version(d[VERSION], '0.3.10'):
            normalizing_to('0.3.10')
            # move TransformConfig fields into TransformConfig.data
            if 'transform' in d:
                if not 'data' in d['transform']:
                    d['transform']['data'] = {}
                d['transform']['data']['matrix'] = d['transform'].pop('matrix')
        if before_version(d[VERSION], '0.3.11'):
            normalizing_to('0.3.11')
            # remove matrix & inverse fields from TransformConfig.data
            if 'transform' in d:
                if 'data' in d['transform']:
                    if 'matrix' in d['transform']['data']:
                        d['transform']['data'].pop('matrix')
                    if 'inverse' in d['transform']['data']:
                        d['transform']['data'].pop('inverse')
        if before_version(d[VERSION], '0.3.12'):
            normalizing_to('0.3.12')
            # flip (bool, bool) to pydantic
            if 'transform' in d:
                if 'flip' in d['transform']:
                    d['transform']['flip'] = {
                        'vertical': d['transform']['flip'][0],
                        'horizontal': d['transform']['flip'][1]
                    }
        if before_version(d[VERSION], '0.3.13'):
            normalizing_to('0.3.13')
            # HsvRangeFilter: radius/c0/c1 -> range/color
            if 'masks' in d:
                for mask in d['masks']:
                    if 'filter' in mask and mask['filter'][
                            'type'] == 'HsvRangeFilter':
                        if 'radius' in mask['filter']['data']:
                            mask['filter']['data']['range'] = mask['filter'][
                                'data'].pop('radius')
                        if 'c0' in mask['filter']['data']:
                            c0 = mask['filter']['data'].pop('c0')
                        if 'c1' in mask['filter']['data']:
                            mask['filter']['data'].pop('c1')
        if before_version(d[VERSION], '0.3.15'):
            normalizing_to('0.3.15')
            # rename parameters -> feature_parameters
            if 'parameters' in d:
                d['feature_parameters'] = d.pop('parameters')

    else:
        raise NotImplementedError

    # Remove non-standard fields
    config_type = ConfigType(d[CLASS]).get()
    for k in list(d.keys()):
        if k not in (VERSION, CLASS):
            if not k in config_type.__fields__:
                log.warning(f"Removed unexpected attribute "
                            f"'{k}':{d.pop(k)} from {d[CLASS]}")

    # Remove timestamp & version info
    d.pop('timestamp', None)
    d.pop('version', None)

    untag(d)

    return d
コード例 #8
0
 def mean_color(self, filter: HsvRangeFilterConfig) -> Color:
     # S and V are arbitrary but work relatively well
     # for both overlay & plot colors
     return HsvColor(h=filter.color.h, s=255, v=200)
コード例 #9
0
 def ready(self) -> bool:
     return self.color != HsvColor()
コード例 #10
0
ファイル: BackgroundFilter.py プロジェクト: ybnd/isimple
import numpy as np
import cv2

from isimple import get_logger, settings
from isimple.config import extend, ConfigType, Field

from isimple.core.interface import FilterConfig, FilterInterface, FilterType

from isimple.maths.colors import Color, HsvColor, convert, WRAP

log = get_logger(__name__)


COLOR = HsvColor(h=0, s=0, v=0)


@extend(ConfigType)
class BackgroundFilterConfig(FilterConfig):
    """HSV range filter"""
    range: HsvColor = Field(default=HsvColor(h=10, s=75, v=75))
    color: HsvColor = Field(default=HsvColor())

    @property
    def ready(self) -> bool:
        return self.color != HsvColor()

    @property
    def c0(self) -> HsvColor:
        return self.color - self.range

    @property