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/analysis-should-prefer-user-instances
  • Loading branch information
roomrys committed Jun 13, 2022
2 parents e5b6a3d + 7b9d854 commit e000174
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 4 deletions.
5 changes: 4 additions & 1 deletion sleap/gui/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -935,6 +935,7 @@ def _add_button(to, label, action, key=None):
videos_layout.addWidget(self.videosTable)

hb = QHBoxLayout()
_add_button(hb, "Toggle Grayscale", self.commands.toggleGrayscale)
_add_button(hb, "Show Video", self.videosTable.activateSelected)
_add_button(hb, "Add Videos", self.commands.addVideo)
_add_button(hb, "Remove Video", self.commands.removeVideo)
Expand Down Expand Up @@ -1172,9 +1173,10 @@ def overlay_state_connect(overlay, state_key, overlay_attribute=None):
def _update_gui_state(self):
"""Enable/disable gui items based on current state."""
has_selected_instance = self.state["instance"] is not None
has_selected_video = self.state["selected_video"] is not None
has_selected_node = self.state["selected_node"] is not None
has_selected_edge = self.state["selected_edge"] is not None
has_selected_video = self.state["selected_video"] is not None
has_video = self.state["video"] is not None

has_frame_range = bool(self.state["has_frame_range"])
has_unsaved_changes = bool(self.state["has_changes"])
Expand Down Expand Up @@ -1224,6 +1226,7 @@ def _update_gui_state(self):
self._buttons["add edge"].setEnabled(has_nodes_selected)
self._buttons["delete edge"].setEnabled(has_selected_edge)
self._buttons["delete node"].setEnabled(has_selected_node)
self._buttons["toggle grayscale"].setEnabled(has_video)
self._buttons["show video"].setEnabled(has_selected_video)
self._buttons["remove video"].setEnabled(has_selected_video)
self._buttons["delete instance"].setEnabled(has_selected_instance)
Expand Down
28 changes: 28 additions & 0 deletions sleap/gui/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,10 @@ def gotoVideoAndFrame(self, video: Video, frame_idx: int):

# Editing Commands

def toggleGrayscale(self):
"""Toggles grayscale setting for current video."""
self.execute(ToggleGrayscale)

def addVideo(self):
"""Shows gui for adding videos to project."""
self.execute(AddVideo)
Expand Down Expand Up @@ -1442,6 +1446,30 @@ class EditCommand(AppCommand):
does_edits = True


class ToggleGrayscale(EditCommand):
topics = [UpdateTopic.video, UpdateTopic.frame]

@staticmethod
def do_action(context: CommandContext, params: dict):
"""Reset the video backend."""
video: Video = context.state["video"]
try:
grayscale = video.backend.grayscale
video.backend.reset(grayscale=(not grayscale))
except:
print(
f"This video type {type(video.backend)} does not support grayscale yet."
)

@staticmethod
def ask(context: CommandContext, params: dict) -> bool:
"""Check that video can be reset."""
# Check that current video is set
if context.state["video"] is None:
return False
return True


class AddVideo(EditCommand):
topics = [UpdateTopic.video]

Expand Down
18 changes: 16 additions & 2 deletions sleap/io/video.py
Original file line number Diff line number Diff line change
Expand Up @@ -461,9 +461,23 @@ def dtype(self):
"""See :class:`Video`."""
return self.test_frame.dtype

def reset(self):
def reset(self, filename: str = None, grayscale: bool = None, bgr: bool = None):
"""Reloads the video."""
self._reader_ = None
if filename is not None:
self.filename = filename
self._test_frame_ = None # Test frame does not depend on num channels

if grayscale is not None:
self.grayscale = grayscale
self._detect_grayscale = False
else:
self._detect_grayscale = True

if bgr is not None:
self.bgr = bgr

if (filename is not None) or (grayscale is not None):
self._reader_ = None # Reader depends on both filename and grayscale

def get_frame(self, idx: int, grayscale: bool = None) -> np.ndarray:
"""See :class:`Video`."""
Expand Down
20 changes: 20 additions & 0 deletions tests/gui/test_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from sleap.instance import Instance

from tests.info.test_h5 import extract_meta_hdf5
from tests.io.test_video import assert_video_params

from pathlib import PurePath, Path
import shutil
Expand Down Expand Up @@ -234,3 +235,22 @@ def assert_videos_written(num_videos: int, labels_path: str = None):
params = {"all_videos": True}
okay = ExportAnalysisFile_ask(context=context, params=params)
assert okay == False


def test_ToggleGrayscale(centered_pair_predictions: Labels):
"""Test functionality for ToggleGrayscale on mp4/avi video"""
labels = centered_pair_predictions
video = labels.video
grayscale = video.backend.grayscale
filename = video.backend.filename

context = CommandContext.from_labels(labels)
context.state["video"] = video

# Toggle grayscale to "not grayscale"
context.toggleGrayscale()
assert_video_params(video=video, filename=filename, grayscale=(not grayscale))

# Toggle grayscale back to "grayscale"
context.toggleGrayscale()
assert_video_params(video=video, filename=filename, grayscale=grayscale)
65 changes: 64 additions & 1 deletion tests/io/test_video.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import pytest
import os
import h5py

import numpy as np

from sleap.io.video import Video, HDF5Video, MediaVideo, DummyVideo, load_video
Expand Down Expand Up @@ -405,3 +404,67 @@ def test_load_video():
video = load_video(TEST_SMALL_CENTERED_PAIR_VID)
assert video.shape == (1100, 384, 384, 1)
assert video[:3].shape == (3, 384, 384, 1)


def assert_video_params(
video: Video,
filename: str,
grayscale: bool = None,
bgr: bool = None,
reset: bool = False,
):
assert video.backend.filename == filename
if grayscale is not None:
assert video.backend.grayscale == grayscale
else:
assert video.backend._detect_grayscale == bool(grayscale is None)
if bgr is not None:
assert video.backend.bgr == bgr
if reset:
assert video.backend._reader_ == None
assert video.backend._test_frame_ == None
# Getting the channels will assert some of the above are not None
if grayscale is not None:
assert video.backend.channels == 3 ** (not grayscale)


def test_reset_video_mp4(small_robot_mp4_vid: Video):

video = small_robot_mp4_vid
filename = video.backend.filename

# Get a frame to set the video parameters in the backend
video.get_frame(idx=0)
assert_video_params(
video=video, filename=filename, grayscale=video.backend.grayscale
)

# Test reset works for color to grayscale

# Reset the backend: grasyscale = True
video.backend.reset(filename=filename, grayscale=True)
assert_video_params(video=video, filename=filename, grayscale=True, reset=True)

# Get a frame to test that reset parameters persist (namely grayscale and channels)
frame = video.get_frame(idx=0)
assert frame.shape[2] == 1
assert_video_params(video=video, filename=filename, grayscale=True)

# Test reset works for grayscale to color

# Reset the backend: grasyscale = False
video.backend.reset(filename=filename, grayscale=False)
assert_video_params(
video=video, filename=filename, grayscale=False, bgr=True, reset=True
)

# Get a frame to test that reset parameters persist (namely grayscale and channels)
frame = video.get_frame(idx=0)
assert frame.shape[2] == 3
assert_video_params(video=video, filename=filename, grayscale=False)

# Test reset works when grayscale is None

# Reset the backend: grayscale = None (and set bgr)
video.backend.reset(filename=filename, bgr=True)
assert_video_params(video=video, filename=filename, bgr=True, reset=True)

0 comments on commit e000174

Please sign in to comment.