Skip to content

Commit

Permalink
Add spectrograms
Browse files Browse the repository at this point in the history
  • Loading branch information
aMarcireau committed May 3, 2023
1 parent 742af00 commit 75aa956
Show file tree
Hide file tree
Showing 11 changed files with 727 additions and 251 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
![banner](banner.png)

Charidotella (https://en.wikipedia.org/wiki/Charidotella_sexpunctata) is a toolbox to organise and visualise Event Stream (.es) recordings.

It supports Python 3.9, 3.10, and 3.11.
Expand Down
Binary file added banner.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
100 changes: 94 additions & 6 deletions charidotella/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@
TASKS: dict[str, tuple[str, task_run]] = {
"colourtime": (tasks.colourtime.EXTENSION, tasks.colourtime.run),
"event_rate": (tasks.event_rate.EXTENSION, tasks.event_rate.run),
"spatiospectrogram": (
tasks.spatiospectrogram.EXTENSION,
tasks.spatiospectrogram.run,
),
"spectrogram": (tasks.spectrogram.EXTENSION, tasks.spectrogram.run),
"video": (tasks.video.EXTENSION, tasks.video.run),
"wiggle": (tasks.wiggle.EXTENSION, tasks.wiggle.run),
}
Expand Down Expand Up @@ -90,6 +95,12 @@ def main():
action="store_true",
help="Do not generate new names for the recordings",
)
init_parser.add_argument(
"--spatiospectrograms",
"-s",
action="store_true",
help="Generate spatio-spectrogram tasks",
)
run_parser = subparsers.add_parser("run", help="Process a configuration file")
run_parser.add_argument(
"--configuration",
Expand Down Expand Up @@ -381,9 +392,11 @@ def run_generators(configuration: dict[str, typing.Any]):
"tasks": [
"colourtime-.+",
"event-rate-.+",
"spectrogram",
"wiggle-.+",
"video-real-time",
],
]
+ (["spatiospectrogram"] if args.spatiospectrograms else []),
}
)
with open(
Expand Down Expand Up @@ -471,9 +484,63 @@ def run_generators(configuration: dict[str, typing.Any]):
)

configuration_file.write("\n\n# tasks configuration\n\n")
tasks = {
"spectrogram": {
"type": "spectrogram",
"icon": "🎻",
"tau": utilities.timestamp_to_timecode(100000),
"mode": "all",
"maximum": 10000.0,
"frequencies": 100,
"times": 1000,
"gamma": 0.5,
},
"video-real-time": {
"type": "video",
"icon": "🎬",
"frametime": utilities.timestamp_to_timecode(20000),
"tau": utilities.timestamp_to_timecode(200000),
"style": "exponential",
"on_color": "#F4C20D",
"off_color": "#1E88E5",
"idle_color": "#191919",
"cumulative_ratio": 0.01,
"timecode": True,
"h264_crf": 15,
"ffmpeg": "ffmpeg",
"scale": 1,
},
}
toml.dump(
{
"tasks": {
"spatiospectrogram": {
"type": "spatiospectrogram",
"icon": "🎸",
"frametime": utilities.timestamp_to_timecode(20000),
"scale": 1,
"tau": utilities.timestamp_to_timecode(100000),
"mode": "all",
"minimum": 10.0,
"maximum": 10000.0,
"frequencies": 100,
"frequency-gamma": 0.5,
"amplitude-gamma": 0.5,
"discard": 0.001,
"timecode": True,
"h264_crf": 15,
"ffmpeg": "ffmpeg",
},
"spectrogram": {
"type": "spectrogram",
"icon": "🎻",
"tau": utilities.timestamp_to_timecode(100000),
"mode": "all",
"maximum": 10000.0,
"frequencies": 100,
"times": 1000,
"gamma": 0.5,
},
"video-real-time": {
"type": "video",
"icon": "🎬",
Expand All @@ -488,7 +555,22 @@ def run_generators(configuration: dict[str, typing.Any]):
"h264_crf": 15,
"ffmpeg": "ffmpeg",
"scale": 1,
}
},
"video-slow-motion": {
"type": "video",
"icon": "🎬",
"frametime": utilities.timestamp_to_timecode(2000),
"tau": utilities.timestamp_to_timecode(20000),
"style": "exponential",
"on_color": "#F4C20D",
"off_color": "#1E88E5",
"idle_color": "#191919",
"cumulative_ratio": 0.01,
"timecode": True,
"h264_crf": 15,
"ffmpeg": "ffmpeg",
"scale": 1,
},
},
},
configuration_file,
Expand Down Expand Up @@ -539,8 +621,8 @@ def run_generators(configuration: dict[str, typing.Any]):
"axis_color": "#000000",
"main_grid_color": "#555555",
"secondary_grid_color": "#DDDDDD",
"width": 1920,
"height": 1080,
"width": 1280,
"height": 720,
},
},
{
Expand All @@ -556,7 +638,7 @@ def run_generators(configuration: dict[str, typing.Any]):
"template": {
"name": "wiggle-@suffix",
"type": "wiggle",
"icon": "🌀",
"icon": "👋",
"forward_duration": "@raw(forward_duration)",
"tau_to_frametime_ratio": 3.0,
"style": "cumulative",
Expand Down Expand Up @@ -616,9 +698,15 @@ def run_generators(configuration: dict[str, typing.Any]):
"tasks": [
"colourtime-.+",
"event-rate-.+",
"spectrogram",
"wiggle-.+",
"video-real-time",
],
]
+ (
["spatiospectrogram"]
if args.spatiospectrograms
else []
),
},
}
]
Expand Down
Empty file removed charidotella/setup.py
Empty file.
2 changes: 2 additions & 0 deletions charidotella/tasks/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from . import colourtime as colourtime
from . import event_rate as event_rate
from . import spatiospectrogram as spatiospectrogram
from . import spectrogram as spectrogram
from . import video as video
from . import wiggle as wiggle
107 changes: 107 additions & 0 deletions charidotella/tasks/spatiospectrogram.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import atexit
import importlib.resources
import pathlib
import subprocess
import typing

EXTENSION = ".mp4"


def run(
input: pathlib.Path,
output: pathlib.Path,
begin: int,
end: int,
parameters: dict[str, typing.Any],
):
width, height = (
int(value)
for value in subprocess.run(
[
str(importlib.resources.files("charidotella").joinpath("assets/size")),
str(input),
],
check=True,
capture_output=True,
).stdout.split(b"x")
)
width *= parameters["scale"]
height *= parameters["scale"]
spatiospectrogram_arguments = [
str(
importlib.resources.files("charidotella").joinpath(
"assets/spatiospectrogram"
)
),
f"--input={input}",
f"--begin={begin}",
f"--end={end}",
f"--frametime={parameters['frametime']}",
f"--scale={parameters['scale']}",
f"--tau={parameters['tau']}",
f"--mode={parameters['mode']}",
f"--minimum={parameters['minimum']}",
f"--maximum={parameters['maximum']}",
f"--frequencies={parameters['frequencies']}",
f"--frequency-gamma={parameters['frequency-gamma']}",
f"--amplitude-gamma={parameters['amplitude-gamma']}",
f"--discard={parameters['discard']}",
]
if parameters["timecode"]:
spatiospectrogram_arguments.append("--add-timecode")
spatiospectrogram = subprocess.Popen(
spatiospectrogram_arguments,
stdout=subprocess.PIPE,
)
assert spatiospectrogram.stdout is not None
ffmpeg = subprocess.Popen(
[
parameters["ffmpeg"],
"-hide_banner",
"-loglevel",
"warning",
"-stats",
"-f",
"rawvideo",
"-s",
f"{width}x{height}",
"-framerate",
"50",
"-pix_fmt",
"rgb24",
"-i",
"-",
"-c:v",
"libx264",
"-pix_fmt",
"yuv420p",
"-crf",
str(parameters["h264_crf"]),
"-f",
"mp4",
"-y",
str(output),
],
stdin=subprocess.PIPE,
)
assert ffmpeg.stdin is not None
frame_size = width * height * 3

def cleanup():
if es_to_frames is not None:
es_to_frames.kill()
if ffmpeg is not None:
ffmpeg.kill()

atexit.register(cleanup)
while True:
frame = spatiospectrogram.stdout.read(frame_size)
if len(frame) != frame_size:
break
ffmpeg.stdin.write(frame)
ffmpeg.stdin.close()
spatiospectrogram.wait()
es_to_frames = None
ffmpeg.wait()
ffmpeg = None
atexit.unregister(cleanup)
40 changes: 40 additions & 0 deletions charidotella/tasks/spectrogram.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import importlib.resources
import pathlib
import subprocess
import typing

EXTENSION = ".png"


def run(
input: pathlib.Path,
output: pathlib.Path,
begin: int,
end: int,
parameters: dict[str, typing.Any],
):
arguments = [
str(importlib.resources.files("charidotella").joinpath("assets/spectrogram")),
str(input),
str(output),
str(output.with_suffix(".json")),
f"--begin={begin}",
f"--end={end}",
f"--tau={parameters['tau']}",
f"--mode={parameters['mode']}",
f"--maximum={parameters['maximum']}",
f"--frequencies={parameters['frequencies']}",
f"--times={parameters['times']}",
f"--gamma={parameters['gamma']}",
]
if "minimum" in parameters:
arguments.append(f"--minimum={parameters['minimum']}")
if "region-of-interest" in parameters:
arguments.append(f"--left={parameters['region-of-interest'][0]}")
arguments.append(f"--bottom={parameters['region-of-interest'][1]}")
arguments.append(f"--width={parameters['region-of-interest'][2]}")
arguments.append(f"--height={parameters['region-of-interest'][3]}")
subprocess.run(
arguments,
check=True,
)
2 changes: 1 addition & 1 deletion charidotella/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.10"
__version__ = "1.0"
Loading

0 comments on commit 75aa956

Please sign in to comment.