Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added fill source and filter #101

Merged
merged 2 commits into from
Mar 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ The following inputs are currently supported:
| image | Static image (e.g. `.png`) |
| video | Video (e.g. `.mp4`) |
| webcam | Linux Webcam (`/dev/videoN`) |
| fill | Fills a new image with a color |
| youtube | YouTube stream (e.g. `https://youtu.be/f0cGgOv3l4c`, see [example config](https://github.com/de-code/layered-vision/tree/develop/example-config/display-video-bodypix-replace-background-youtube.yml)) |
| mss | Screen capture using [mss](https://python-mss.readthedocs.io/index.html) (see [example config](https://github.com/de-code/layered-vision/tree/develop/example-config/display-video-bodypix-replace-background-mss.yml)) |

Expand Down Expand Up @@ -159,6 +160,7 @@ The following filters are currently supported:
| `bilateral` | Applies a [bilateral filter](https://en.wikipedia.org/wiki/Bilateral_filter), using `d`, `sigma_color` and `sigma_space` parameters. |
| `motion_blur` | Adds a motion blur to the image or channel. That could be used to make an alpha mask move more slowly |
| `pixelate` | Pixelates the input. |
| `fill` | Fills the input or a selected channel with a color / value. e.g. with `color` set to `blue` |
| `invert` | Inverts the input. e.g. `black` to `white` |
| `multiply` | Multiplies the input with a constant value. e.g. to adjust the `alpha` channel |
| `warp_perspective` | Warps the perspective of the input image given a list of `target_points`. e.g. to display it in a corner of the output image |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ layers:
input_path: "https://www.dropbox.com/s/oqftndbs29g8ekd/carnival-rides-operating-in-an-amusement-park-3031943-360p.mp4?dl=1"
repeat: true
preload: true
- layers:
- id: bg_fill
input_path: "fill:"
color: blue
repeat: true
resize_like_id: in
no_source: true
enabled: false
- layers:
- id: bg2
enabled: false
Expand Down
54 changes: 54 additions & 0 deletions layered_vision/filters/fill.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import logging
from typing import NamedTuple, Optional

import numpy as np

from layered_vision.utils.image import ImageArray
from layered_vision.filters.api import AbstractOptionalChannelFilter
from layered_vision.config import LayerConfig
from layered_vision.utils.colors import get_color_numpy_array


LOGGER = logging.getLogger(__name__)


class FillFilter(AbstractOptionalChannelFilter):
class Config(NamedTuple):
color: Optional[np.ndarray]
value: Optional[int]

def __init__(self, layer_config: LayerConfig, **kwargs):
super().__init__(layer_config, **kwargs)
self.fill_config = self.parse_fill_config(layer_config)

def parse_fill_config(self, layer_config: LayerConfig) -> Config:
color = layer_config.get('color')
color_value: Optional[np.ndarray] = get_color_numpy_array(color)
value = layer_config.get_int('value')
config = FillFilter.Config(
color=color_value,
value=value
)
LOGGER.info('fill config: %s', config)
assert config.color is not None or config.value is not None
return config

def on_config_changed(self, layer_config: LayerConfig):
super().on_config_changed(layer_config)
self.fill_config = self.parse_fill_config(layer_config)

def do_channel_filter(self, image_array: ImageArray) -> ImageArray:
LOGGER.debug('fill, image_array dtype: %s', image_array.dtype)
config = self.fill_config
assert config.color
image_shape = image_array.shape
color_channels = image_shape[-1]
if color_channels == 1:
return np.full_like(image_array, config.value)
return np.full(
(image_shape[0], image_shape[1], len(config.color)),
config.color
)


FILTER_CLASS = FillFilter
2 changes: 2 additions & 0 deletions layered_vision/filters/warp_perspective.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ def do_channel_filter(self, image_array: ImageArray) -> ImageArray:
image_array,
np.full_like(image_array[:, :, 0], 255),
))
if np.issubdtype(image_array.dtype, np.integer):
image_array = image_array.astype(np.float)
return cv2.warpPerspective(
image_array,
transformation_matrix,
Expand Down
41 changes: 41 additions & 0 deletions layered_vision/sources/fill.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import logging
from contextlib import contextmanager
from itertools import cycle
from typing import Iterable, Iterator

import numpy as np

from layered_vision.utils.image import ImageArray, ImageSize
from layered_vision.sources.api import T_ImageSourceFactory, T_ImageSource
from layered_vision.utils.colors import get_color_numpy_array


LOGGER = logging.getLogger(__name__)


@contextmanager
def get_fill_image_source(
path: str, # pylint: disable=unused-argument
*args,
image_size: ImageSize = None,
repeat: bool = False,
color,
**_
) -> Iterator[Iterable[ImageArray]]:
color_value = get_color_numpy_array(color)
if color_value is None:
raise RuntimeError('color required')
if image_size is None:
raise RuntimeError('image size required')
LOGGER.info('fill input: color=%r (image_size=%s)', color_value, image_size)
image_array = np.full(
(image_size.height, image_size.width, len(color_value)),
color_value
)
image_array_iterable: T_ImageSource = [image_array]
if repeat:
image_array_iterable = cycle(image_array_iterable)
yield image_array_iterable


IMAGE_SOURCE_FACTORY: T_ImageSourceFactory = get_fill_image_source
22 changes: 22 additions & 0 deletions layered_vision/utils/colors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from typing import Optional

import numpy as np


NAMED_COLORS = {
'black': (0, 0, 0),
'white': (255, 255, 255),
'red': (255, 0, 0),
'green': (0, 255, 0),
'blue': (0, 0, 255)
}


def get_color_numpy_array(color) -> Optional[np.ndarray]:
if color:
if color in NAMED_COLORS:
return np.asarray(NAMED_COLORS[color])
if isinstance(color, (tuple, list,)):
return np.asarray(color)
raise ValueError('unsupported color value type: %r (%r)' % (type(color), color))
return None
3 changes: 3 additions & 0 deletions layered_vision/utils/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ class SimpleImageArray:
def __getitem__(self, *args) -> Union['SimpleImageArray', int, float]:
pass

def astype(self, dtype: Any) -> 'SimpleImageArray':
pass


ImageArray = Union[np.ndarray, SimpleImageArray]

Expand Down