Skip to content

Commit

Permalink
Cleaner version with executable
Browse files Browse the repository at this point in the history
  • Loading branch information
aMarcireau committed Mar 23, 2023
1 parent 5f02c2b commit 040df96
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 53 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ __pycache__
bin
lib
share
etc
*.so
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
[package]
name = "colourtime_extension"
name = "colourtime"
version = "0.1.0"
edition = "2018"

[lib]
name = "colourtime_extension"
name = "colourtime"
crate-type = ["cdylib"]

[dependencies]
Expand Down
25 changes: 22 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,32 @@
# colourtime
```sh
pip install colourtime
```

Colourtime converts an Event Stream file (.es) into a PNG image, where time is represented with colour gradients.

The pip package installs a Python library (`import colourtime`) and command-line executable (`colourtime`).

The following command converts an Event Stream file.

```sh
colourtime /path/to/input.es
```

A Python script to represent event data time with colours
Run `colourtime --help` to list available options.

Check **python/**init**.py** for details on the Python API.

## Build from source

```sh
python3 -m venv .
source ./bin/activate
pip3 install -U pip event_stream matplotlib maturin numpy pillow
maturin develop
maturin develop -r
```

## Format

```
isort .; black .; pyright .
```
19 changes: 18 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,21 @@ readme = "README.md"
license = {text = "MIT"}
classifiers = ["Programming Language :: Python :: 3"]
dependencies = ["event_stream", "matplotlib", "numpy", "pillow"]
version = "0.1.1"
version = "0.2"

[project.scripts]
colourtime = "colourtime:main"

[tool.maturin]
python-source = "python"

[tool.black]
extend-exclude = "lib"

[tool.pyright]
typeCheckingMode = "basic"
exclude = ["lib"]

[tool.isort]
profile = "black"
skip = "lib"
99 changes: 54 additions & 45 deletions colourtime.py → python/colourtime/__init__.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
import event_stream
import colourtime_extension
import matplotlib.colors
import matplotlib
import matplotlib.markers
import matplotlib.pyplot
import re
import typing

import numpy
import numpy.lib.recfunctions
import numpy.typing
import PIL.Image
import re
import typing

matplotlib.use("agg")
from . import colourtime


def convert(
Expand All @@ -20,15 +15,17 @@ def convert(
width: int,
height: int,
decoder: typing.Generator[numpy.ndarray, None, None],
colormap: matplotlib.colors.Colormap,
colormap: typing.Callable[
[numpy.typing.NDArray[numpy.float64]], numpy.typing.NDArray[numpy.float64]
],
time_mapping: typing.Callable[
[numpy.typing.NDArray[numpy.uint64]], numpy.typing.NDArray[numpy.float64]
],
alpha: float,
background_color: tuple[float, float, float, float],
background_colour: tuple[float, float, float, float],
) -> PIL.Image.Image:
image = numpy.zeros((width, height, 4), dtype=numpy.float64)
image[:, :] = background_color
image[:, :] = background_colour
for packet in decoder:
if begin is not None and packet["t"][-1] < begin:
continue
Expand All @@ -51,11 +48,13 @@ def convert(
(numpy.uint16, 2)
)
colours = colormap(time_mapping(events["t"]))
colourtime_extension.stack(image, xy, colours, alpha)
colourtime.stack(image, xy, colours, alpha) # type: ignore
return PIL.Image.fromarray(
numpy.round(image * 255.0).astype(numpy.uint8),
mode="RGBA",
).transpose(PIL.Image.Transpose.ROTATE_90)
).transpose(
PIL.Image.Transpose.ROTATE_90 # type: ignore
)


def generate_cyclic_time_mapping(duration: int, begin: int):
Expand All @@ -78,31 +77,6 @@ def linear_time_mapping(
return linear_time_mapping


TIMECODE_PATTERN = re.compile(r"^(\d+):(\d+):(\d+)(?:\.(\d+))?$")


def timecode(value: str) -> int:
if value.isdigit():
return int(value)
match = TIMECODE_PATTERN.match(value)
if match is None:
raise argparse.ArgumentTypeError(
"expected an integer or a timecode (12:34:56.789000)"
)
result = (
int(match[1]) * 3600000000 + int(match[2]) * 60000000 + int(match[3]) * 1000000
)
if match[4] is not None:
fraction_string: str = match[4]
if len(fraction_string) == 6:
result += int(fraction_string)
elif len(fraction_string) < 6:
result += int(fraction_string + "0" * (6 - len(fraction_string)))
else:
result += round(float("0." + fraction_string) * 1e6)
return result


def find_begin_and_end(
decoder: typing.Generator[numpy.ndarray, None, None],
find_end: bool,
Expand All @@ -129,10 +103,41 @@ def find_begin_and_end(
return (begin, end)


if __name__ == "__main__":
TIMECODE_PATTERN = re.compile(r"^(\d+):(\d+):(\d+)(?:\.(\d+))?$")


def timecode(value: str) -> int:
if value.isdigit():
return int(value)
match = TIMECODE_PATTERN.match(value)
if match is None:
import argparse

raise argparse.ArgumentTypeError(
"expected an integer or a timecode (12:34:56.789000)"
)
result = (
int(match[1]) * 3600000000 + int(match[2]) * 60000000 + int(match[3]) * 1000000
)
if match[4] is not None:
fraction_string: str = match[4]
if len(fraction_string) == 6:
result += int(fraction_string)
elif len(fraction_string) < 6:
result += int(fraction_string + "0" * (6 - len(fraction_string)))
else:
result += round(float("0." + fraction_string) * 1e6)
return result


def main():
import argparse
import pathlib

import event_stream
import matplotlib
import matplotlib.colors

parser = argparse.ArgumentParser(
description="Convert an Event Stream file into a colourtime plot",
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
Expand All @@ -157,7 +162,7 @@ def find_begin_and_end(
)
parser.add_argument(
"--colormap",
"-m",
"-c",
default="viridis",
help="colormap (see https://matplotlib.org/stable/tutorials/colors/colormaps.html)",
)
Expand All @@ -170,15 +175,15 @@ def find_begin_and_end(
)
parser.add_argument(
"--cycle",
"-c",
"-y",
type=timecode,
help="enable cyclic time mapping with the given duration in seconds",
)
parser.add_argument(
"--background-color",
"--background-colour",
"-k",
default="#191919ff",
help="background color (RGB or RGBA)",
help="background colour (RGB or RGBA)",
)
parser.add_argument(
"--png-compression-level",
Expand Down Expand Up @@ -238,7 +243,11 @@ def find_begin_and_end(
colormap=matplotlib.colormaps[args.colormap], # type: ignore
time_mapping=time_mapping,
alpha=args.alpha,
background_color=matplotlib.colors.to_rgba(args.background_color),
background_colour=matplotlib.colors.to_rgba(args.background_colour),
)

image.save(str(output), compress_level=args.png_compression_level)


if __name__ == "__main__":
main()
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ fn stack(
}

#[pyo3::pymodule]
fn colourtime_extension(_py: pyo3::Python<'_>, module: &pyo3::types::PyModule) -> pyo3::PyResult<()> {
fn colourtime(_py: pyo3::Python<'_>, module: &pyo3::types::PyModule) -> pyo3::PyResult<()> {
module.add_function(pyo3::wrap_pyfunction!(stack, module)?)?;
Ok(())
}

0 comments on commit 040df96

Please sign in to comment.