Skip to content

Commit

Permalink
Merge branch 'develop' of https://github.com/talmolab/sleap into liez…
Browse files Browse the repository at this point in the history
…l/render-video-crop
  • Loading branch information
roomrys committed Aug 3, 2022
2 parents 2827106 + 28b395d commit 8d82acf
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 37 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/website.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ on:
# 'main' triggers updates to 'sleap.ai', 'develop' to 'sleap.ai/develop'
- main
- develop
- liezl/docs-nwb
- liezl/convert_output-path
paths:
- "docs/**"
- "README.rst"
Expand Down
25 changes: 23 additions & 2 deletions docs/guides/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,28 @@ positional arguments:
optional arguments:
-h, --help show this help message and exit
-o OUTPUT, --output OUTPUT
Path to output file (optional).
Path to output file (optional). The analysis format expects an
output path per video in the project. Otherwise, the default
naming convention
<slp path>.<video index>_<video filename>.analysis.h5 will be
used for every video without a specified output path. Multiple
outputs can be specified, each preceeded by --output.
Example (analysis format):
Input:
predictions.slp: Path to .slp file to convert which has two
videos:
- first-video.mp4 at video index 0 and
- second-video.mp4 at video index 1.
Command:
sleap-convert predictions.slp --format analysis --output analysis_video_0.h5
Output analysis files:
analysis_video_0.h5: Analysis file for first-video.mp4
(at index 0) in predictions.slp.
predictions.001_second-video.analysis.h5: Analysis file for
second-video.mp4 (at index 1) in predictions.slp. Since
only a single --output argument was specified, the
analysis file for the latter video is given a default name.
--format FORMAT Output format. Default ('slp') is SLEAP dataset;
'analysis' results in analysis.h5 file; 'h5' or 'json'
results in SLEAP dataset with specified file format.
Expand Down Expand Up @@ -292,7 +313,7 @@ optional arguments:
-f FPS, --fps FPS Frames per second for output video (default: 25)
--scale SCALE Output image scale (default: 1.0)
--crop CROP Crop size as <width>,<height> (default: None)
--frames FRAMES List of frames to predict. Either comma separated list (e.g. 1,2,3)
--frames FRAMES List of frames to predict. Either comma separated list (e.g. 1,2,3)
or a range separated by hyphen (e.g. 1-3). (default is entire video)
-video-index VIDEO_INDEX
Index of video in labels dataset (default: 0)
Expand Down
37 changes: 25 additions & 12 deletions sleap/io/convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
Analysis HDF5:
If you want to export an "analysis" h5 file, use `--format analysis`. If no
output path is specified, the default is `<input path>.analysis.h5`.
output path is specified, the default is
`<input path>.<video index>_<video filename>.analysis.h5`.
The analysis HDF5 file has these datasets:
Expand Down Expand Up @@ -54,9 +55,14 @@ def create_parser():
parser.add_argument(
"-o",
"--output",
default="",
help="Path to output file (optional). Note: all analysis files will be written "
"to `output`.<video name>.analysis.h5",
dest="outputs",
action="append",
default=[],
help="Path to output file (optional). The analysis format expects an output "
"path per video in the project. Otherwise, the default naming convention "
"`<slp path>.<video index>_<video filename>.analysis.h5` will be used for "
"every video without a specified output path. Multiple outputs can be "
"specified, each preceeded by --output.",
)
parser.add_argument(
"--format",
Expand Down Expand Up @@ -110,18 +116,25 @@ def main(args: list = None):
if args.format == "analysis":
from sleap.info.write_tracking_h5 import main as write_analysis

output_paths = [path for path in args.outputs]

# Generate filenames if user has not specified (enough) output filenames
labels_path = args.input_path
fn = labels_path if (len(args.output) == 0) else args.output
fn = re.sub("(\.json(\.zip)?|\.h5|\.slp)$", "", fn)
fn = re.sub("(\\.json(\\.zip)?|\\.h5|\\.slp)$", "", labels_path)
fn = PurePath(fn)

for video in labels.videos:
output_path = default_analysis_filename(
default_names = [
default_analysis_filename(
labels=labels,
video=video,
output_path=str(fn.parent),
output_prefix=str(fn.stem),
)
for video in labels.videos[len(args.outputs) :]
]

output_paths.extend(default_names)

for video, output_path in zip(labels.videos, output_paths):
write_analysis(
labels,
output_path=output_path,
Expand All @@ -130,9 +143,9 @@ def main(args: list = None):
video=video,
)

elif args.output:
print(f"Output SLEAP dataset: {args.output}")
Labels.save_file(labels, args.output)
elif len(args.outputs) > 0:
print(f"Output SLEAP dataset: {args.outputs[0]}")
Labels.save_file(labels, args.outputs[0])

elif args.format in ("slp", "h5", "json"):
output_path = f"{args.input_path}.{args.format}"
Expand Down
100 changes: 78 additions & 22 deletions tests/io/test_convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
from sleap.instance import Instance

from pathlib import PurePath, Path
import re
import pytest


def test_analysis_format(
Expand All @@ -16,45 +18,58 @@ def test_analysis_format(
slp_path = PurePath(min_labels_slp_path)
tmpdir = PurePath(tmpdir)

def assert_analysis_existance(output_prefix: str):
output_prefix = PurePath(output_prefix)
for video in labels.videos:
output_name = default_analysis_filename(
def generate_filenames(paths):
output_paths = [path for path in paths]

# Generate filenames if user has not specified (enough) output filenames
labels_path = str(slp_path)
fn = re.sub("(\\.json(\\.zip)?|\\.h5|\\.slp)$", "", labels_path)
fn = PurePath(fn)
default_names = [
default_analysis_filename(
labels=labels,
video=video,
output_path=str(output_prefix.parent),
output_prefix=str(output_prefix.stem),
output_path=str(fn.parent),
output_prefix=str(fn.stem),
)
video_exists = Path(output_name).exists()
for video in labels.videos[len(paths) :]
]

output_paths.extend(default_names)
return output_paths

def assert_analysis_existance(output_paths: list):
output_paths = generate_filenames(output_paths)
for video, path in zip(labels.videos, output_paths):
video_exists = Path(path).exists()
if len(labels.get(video)) == 0:
assert not video_exists
else:
assert video_exists

def sleap_convert_assert(output_prefix, slp_path, with_output: bool = True):
args = (
f"--format analysis -o {output_prefix} {slp_path}".split()
if with_output
else f"--format analysis {slp_path}".split()
)
def sleap_convert_assert(output_paths, slp_path):
output_args = ""
for path in output_paths:
output_args += f"-o {path} "
args = f"--format analysis {output_args}{slp_path}".split()
sleap_convert(args)
assert_analysis_existance(output_prefix)
assert_analysis_existance(output_paths)

# No output specified
output_prefix = slp_path.with_suffix("")
sleap_convert_assert(output_prefix, slp_path, with_output=False)
output_paths = []
sleap_convert_assert(output_paths, slp_path)

# Specify output and retest
output_prefix = tmpdir.with_name("prefix")
sleap_convert_assert(output_prefix, slp_path, with_output=True)
output_paths = [str(tmpdir.with_name("prefix")), str(tmpdir.with_name("prefix2"))]
sleap_convert_assert(output_paths, slp_path)

# Add video and retest
labels.add_video(small_robot_mp4_vid)
slp_path = tmpdir.with_name("new_slp.slp")
labels.save(filename=slp_path)

output_prefix = tmpdir.with_name("prefix")
sleap_convert_assert(output_prefix, slp_path, with_output=True)
output_paths = [str(tmpdir.with_name("prefix"))]
sleap_convert_assert(output_paths, slp_path)

# Add labeled frame to video and retest
labeled_frame = labels.find(video=labels.videos[1], frame_idx=0, return_new=True)[0]
Expand All @@ -64,5 +79,46 @@ def sleap_convert_assert(output_prefix, slp_path, with_output: bool = True):
slp_path = tmpdir.with_name("new_slp.slp")
labels.save(filename=slp_path)

output_prefix = tmpdir.with_name("prefix")
sleap_convert_assert(output_prefix, slp_path, with_output=True)
output_paths = [str(tmpdir.with_name("prefix"))]
sleap_convert_assert(output_paths, slp_path)


def test_sleap_format(
min_labels_slp: Labels,
min_labels_slp_path: Labels,
tmpdir,
):
def sleap_convert_assert(output_path, slp_path):
args = f"-o {output_path} {slp_path}".split()
sleap_convert(args)
assert Path(output_path).exists()

labels = min_labels_slp
slp_path = PurePath(min_labels_slp_path)
tmpdir = PurePath(tmpdir)

output_path = Path(tmpdir, slp_path)
sleap_convert_assert(output_path, slp_path)


@pytest.mark.parametrize("suffix", [".slp", ".json", ".h5"])
def test_auto_slp_h5_json_format(
min_labels_slp: Labels,
min_labels_slp_path: Labels,
tmpdir,
suffix,
):
def sleap_convert_assert(output_path: Path, slp_path):
args = f"--format {output_path.suffix[1:]} {slp_path}".split()
print(f"args = {args}")
sleap_convert(args)
assert Path(output_path).exists()

labels = min_labels_slp
slp_path = PurePath(min_labels_slp_path)
new_slp_path = PurePath(tmpdir, slp_path.name)
labels.save(new_slp_path)

output_path = Path(f"{new_slp_path}{suffix}")
print(f"output_path = {output_path}")
sleap_convert_assert(output_path, new_slp_path)

0 comments on commit 8d82acf

Please sign in to comment.