8000 [WIP] Onboarding UX improvements by arie-matsliah · Pull Request #494 · talmolab/sleap · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

[WIP] Onboarding UX improvements #494

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

Closed
wants to merge 77 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
a27ad8b
Unfinished changes from #484
arie-matsliah Feb 25, 2021
8267cb6
lint
arie-matsliah Feb 25, 2021
f35008f
tmp
arie-matsliah Mar 4, 2021
b481854
Rebase and lint
arie-matsliah Mar 29, 2021
7507608
Cleanup
arie-matsliah Mar 30, 2021
3c72368
Sandbox
arie-matsliah Mar 31, 2021
0309297
Sandbox
arie-matsliah Mar 31, 2021
8b1ee78
Updates
arie-matsliah Apr 1, 2021
941962b
settings tab
arie-matsliah Apr 1, 2021
2811ced
split tabs
arie-matsliah Apr 1, 2021
6a96e75
Update
arie-matsliah Apr 2, 2021
13682b5
Activities package
arie-matsliah Apr 2, 2021
ab6361d
inference + training
arie-matsliah Apr 2, 2021
c02e7f3
MVC
arie-matsliah Apr 2, 2021
f1b66b3
updates
arie-matsliah Apr 5, 2021
5710b8c
black
arie-matsliah Apr 5, 2021
d2430a1
minor
arie-matsliah Apr 5, 2021
212f2bb
print
arie-matsliah Apr 5, 2021
20ee87a
cmd builder
arie-matsliah Apr 6, 2021
5e6d885
Output tab
arie-matsliah Apr 6, 2021
0aee3bf
Output model
arie-matsliah Apr 6, 2021
c9af7d4
Wiring
arie-matsliah Apr 6, 2021
b064555
Enum values
arie-matsliah Apr 6, 2021
7748670
Load values from enums
arie-matsliah Apr 6, 2021
3e645fc
Save config stub
arie-matsliah Apr 6, 2021
418a585
lint
arie-matsliah Apr 6, 2021
acbfd94
Integrate with videos table model
arie-matsliah Apr 6, 2021
4ea657a
Fix
arie-matsliah Apr 6, 2021
88c6cfb
Hook up setters
arie-matsliah Apr 27, 2021
ed91e6f
input states
arie-matsliah Apr 27, 2021
4dab844
input states
arie-matsliah Apr 27, 2021
76d082e
input states
arie-matsliah Apr 27, 2021
51dd4cf
browse
arie-matsliah Apr 27, 2021
182a030
more logic
arie-matsliah Apr 27, 2021
496f72b
Cleanup
arie-matsliah Apr 27, 2021
48bbb7a
Switch to lambdas
arie-matsliah Apr 28, 2021
c64a983
Move widget logic to view
arie-matsliah Apr 28, 2021
209e139
Refactor enums
arie-matsliah Apr 28, 2021
de789d8
cleanup
arie-matsliah Apr 28, 2021
b95271c
Lint
arie-matsliah Apr 28, 2021
0e7d42e
remove unused
arie-matsliah Apr 28, 2021
f2d8647
Model loading
arie-matsliah Apr 28, 2021
9f6af68
Pass content on save
arie-matsliah Apr 28, 2021
41a14b4
arg and display
arie-matsliah Apr 28, 2021
921c488
Load model
arie-matsliah Apr 28, 2021
7b1c9c4
Videos widget
arie-matsliah Apr 28, 2021
60b4959
fix
arie-matsliah Apr 28, 2021
577dc3d
Lint
arie-matsliah Apr 28, 2021
7a8fb11
Simplify
arie-matsliah Apr 29, 2021
c916587
Rename
arie-matsliah Apr 29, 2021
df2b2ec
Minor
arie-matsliah Apr 29, 2021
ee939ef
Training config dialog
arie-matsliah Apr 29, 2021
b818d36
Training config dialog
arie-matsliah Apr 29, 2021
6432adf
Minor
arie-matsliah Apr 29, 2021
14eea3f
Error handling
arie-matsliah Apr 29, 2021
72032bd
Runner
arie-matsliah Apr 29, 2021
d5c88d3
Tests
arie-matsliah Apr 29, 2021
8000
6f0cb72
Minor
arie-matsliah May 3, 2021
1e56c2d
Executor
arie-matsliah May 3, 2021
0e7dae1
Dbg
arie-matsliah May 3, 2021
ae01e7d
launch function
arie-matsliah May 3, 2021
1d3f809
Launcher
arie-matsliah May 3, 2021
093b5cb
batch inference
arie-matsliah May 3, 2021
4a6e1ef
Update title
arie-matsliah May 3, 2021
20afa4e
Allow selecting frames in videos table
arie-matsliah May 3, 2021
2a1a504
logo
arie-matsliah May 3, 2021
b71fd50
Progress viewer with cancel
arie-matsliah May 3, 2021
f7854c0
Progress updates
arie-matsliah May 4, 2021
58c185e
Cleanup
arie-matsliah May 4, 2021
7b99db8
Remove verbosity from UI
arie-matsliah May 4, 2021
fe64de6
Stopping
arie-matsliah May 4, 2021
7a1b691
cleanup
arie-matsliah May 4, 2021
650cfdb
Remove mock data
arie-matsliah May 4, 2021
0b0ee2b
cleanup
arie-matsliah May 4, 2021
20ef0a0
remove unrelated changes
arie-matsliah May 4, 2021
adb93ff
lint
arie-matsliah May 4, 2021
f7a47f3
Diff lint in CI
arie-matsliah May 4, 2021
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: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jobs:
pip install black
- name: Run Black
run: |
black --check sleap tests
black --check sleap tests --diff
tests:
name: Tests (${{ matrix.os }})
runs-on: ${{ matrix.os }}
Expand Down
2 changes: 1 addition & 1 deletion docs/guides/cli.rst
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ If you specify how many identities there should be in a frame (i.e., the number
--open-in-gui Open the resulting predictions in the GUI when
finished.
--tracking.tracker TRACKING.TRACKER
Options: simple, flow, None (default: None)
Options: simple, flow, kalman, None (default: None)
--tracking.target_instance_count TRACKING.TARGET_INSTANCE_COUNT
Target number of instances to track per frame.
(default: 0)
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def get_requirements(require_name=None):
"sleap-convert=sleap.io.convert:main",
"sleap-label=sleap.gui.app:main",
"sleap-train=sleap.nn.training:main",
"sleap-track=sleap.nn.inference:main",
"sleap-track=sleap.gui.activities.inference.view:main",
"sleap-inspect=sleap.info.labels:main",
"sleap-diagnostic=sleap.diagnostic:main",
],
Expand Down
Empty file.
Empty file.
256 changes: 256 additions & 0 deletions sleap/gui/activities/inference/controller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
import json
import os
import subprocess
import time
from json import JSONDecodeError
from typing import List, Callable, Optional

import attr

from sleap.gui.activities.inference.model import (
InferenceGuiModel,
)
from sleap.gui.activities.inference.enums import ModelType, Verbosity, TrackerType
from sleap.gui.learning.runners import kill_process


@attr.s(auto_attribs=True)
class InferenceGuiController(object):
model: InferenceGuiModel
logging_enabled: bool = True

# Getters

def get_model_type(self) -> ModelType:
return self.model.models.model_type

def get_single_instance_model_path(self) -> str:
return self.model.models.single_instance_model_path

def get_bottom_up_model_path(self) -> str:
return self.model.models.bottom_up_model_path

def get_top_down_centroid_model_path(self) -> str:
return self.model.models.centroid_model_path

def get_top_down_centered_instance_model_path(self) -> str:
return self.model.models.centered_instance_model_path

def get_video_paths(self) -> List[str]:
return self.model.videos.paths

def get_video_frames(self) -> List[str]:
return self.model.videos.frames

def get_max_num_instances_in_frame(self) -> int:
return self.model.instances.max_num_instances

def get_enable_tracking(self) -> bool:
return self.model.instances.enable_tracking

def get_tracking_method(self) -> TrackerType:
return self.model.instances.tracking_method

def get_tracking_window_size(self) -> int:
return self.model.instances.tracking_window

def get_output_dir_path(self) -> str:
return self.model.output.output_dir_path

def get_output_file_suffix(self) -> str:
return self.model.output.output_file_suffix

def get_include_empty_frames(self) -> bool:
return self.model.output.include_empty_frames

def get_verbosity(self) -> Verbosity:
return self.model.output.verbosity

# Setters

def set_model_type(self, value: ModelType) -> None:
self.model.models.model_type = value

def set_single_instance_model_path(self, value: str) -> None:
self.model.models.single_instance_model_path = value

def set_bottom_up_model_path(self, value: str) -> None:
self.model.models.bottom_up_model_path = value

def set_top_down_centroid_model_path(self, value: str) -> None:
self.model.models.centroid_model_path = value

def set_top_down_centered_instance_model_path(self, value: str) -> None:
self.model.models.centered_instance_model_path = value

def set_video_paths(self, value: List[str]) -> None:
self.model.videos.paths = value

def set_video_frames(self, value: List[str]) -> None:
self.model.videos.frames = value

def set_max_num_instances_in_frame(self, value: int) -> None:
self.model.instances.max_num_instances = value

def set_enable_tracking(self, value: bool) -> None:
self.model.instances.enable_tracking = value

def set_tracking_method(self, value: TrackerType) -> None:
self.model.instances.tracking_method = value

def set_tracking_window_size(self, value: int) -> None:
self.model.instances.tracking_window = value

def set_output_dir_path(self, value: str) -> None:
self.model.output.output_dir_path = value

def set_output_file_suffix(self, value: str) -> None:
self.model.output.output_file_suffix = value

def set_include_empty_frames(self, value: bool) -> None:
self.model.output.include_empty_frames = value

def set_verbosity(self, value: Verbosity) -> None:
self.model.output.verbosity = value

# Actions

def run(self, content: dict, callback: Callable[[dict], bool]) -> None:
self.save(content=content)
statuses = []
num_videos = len(self.get_video_paths())
for video_idx in range(num_videos):
video_path = self.get_video_paths()[video_idx]
frames = self.get_video_frames()[video_idx]
cancel_request = callback(
dict(
video=f"{video_path} ({video_idx + 1} out of {num_videos})",
n_processed=0,
n_total=frames,
)
)
if cancel_request:
break
cmd_args = ["sleap-track", video_path]
if frames:
cmd_args.extend(["--frames", frames])

if self.get_model_type() == ModelType.TOP_DOWN:
cmd_args.extend(
["-m", os.path.dirname(self.get_top_down_centroid_model_path())]
)
cmd_args.extend(
[
"-m",
os.path.dirname(
self.get_top_down_centered_instance_model_path()
),
]
)
elif self.get_model_type() == ModelType.BOTTOM_UP:
cmd_args.extend(
["-m", os.path.dirname(self.get_bottom_up_model_path())]
)
elif self.get_model_type() == ModelType.SINGLE_INSTANCE:
cmd_args.extend(
["-m", os.path.dirname(self.get_single_instance_model_path())]
)

if self.get_enable_tracking():
cmd_args.extend(
["--tracking.tracker", self.get_tracking_method().arg()]
)
cmd_args.extend(
["--tracking.track_window", self.get_tracking_window_size()]
)
cmd_args.extend(
[
"--tracking.target_instance_count",
self.get_max_num_instances_in_frame(),
]
)

output_file_path = (
f"{os.path.basename(video_path)}{self.get_output_file_suffix()}"
)
if self.get_output_dir_path():
output_file_path = os.path.join(
self.get_output_dir_path(), output_file_path
)
cmd_args.extend(["-o", output_file_path])

cmd_args.extend(["--verbosity", self.get_verbosity().arg()])

if not self.get_include_empty_frames():
cmd_args.append("--no-empty-frames")

def process_output_line(line: str) -> bool:
try:
output = json.loads(line)
output[
"video"
] = f"{video_path} ({video_idx + 1} out of {num_videos})"
return callback(output)
except JSONDecodeError:
# not json, pass thru
self.log(line)
return False

status = self._execute(
cmd_args=cmd_args, output_consumer=lambda o: process_output_line(o)
)
statuses.append(status)

callback({"status": statuses})

def save(self, content: dict) -> None:
self.log(f"Saving: {content}")
self.set_model_type(ModelType.from_display(content["model_type"])),
self.set_single_instance_model_path(content["single_instance_model"]),
self.set_bottom_up_model_path(content["bottom_up_model"]),
self.set_top_down_centroid_model_path(content["top_down_centroid_model"]),
self.set_top_down_centered_instance_model_path(
content["top_down_centered_instance_model"]
),
self.set_video_paths(content["video_paths"]),
self.set_video_frames(content["video_frames"]),
self.set_max_num_instances_in_frame(content["max_num_instances_in_frame"]),
self.set_enable_tracking(content["enable_tracking"]),
self.set_tracking_method(TrackerType.from_display(content["tracking_method"])),
self.set_tracking_window_size(content["tracking_window_size"]),
self.set_output_dir_path(content["output_dir_path"]),
self.set_output_file_suffix(content["output_file_suffix"]),
self.set_include_empty_frames(content["include_empty_frames"]),
self.set_verbosity(Verbosity.JSON),

def export(self):
self.log(f"Export stub...")

def load(self):
self.log(f"Load stub...")

def _execute(
self,
cmd_args: List[str],
output_consumer: Optional[Callable[[str], int]] = None,
) -> int:
self.log(f"Executing: {' '.join(cmd_args)}\n")
with subprocess.Popen(cmd_args, stdout=subprocess.PIPE) as proc:
while proc.poll() is None:
output_line = proc.stdout.readline().decode().rstrip()
cancel_request = output_consumer(output_line)
if cancel_request:
self.log(
f"Received cancel request from callback. Cancelling {proc.pid}.."
)
kill_process(proc.pid)
return -1
time.sleep(0.1)
self.log(f"Process return code: {proc.returncode}")
return proc.returncode

# Utils

def log(self, message: str) -> None:
if self.logging_enabled:
print(message)
33 changes: 33 additions & 0 deletions sleap/gui/activities/inference/enums.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from enum import Enum


class BaseDisplayEnum(Enum):
def display(self) -> str:
return self.value if isinstance(self.value, str) else self.value[0]

def arg(self) -> str:
return self.value[1]

@classmethod
def from_display(cls, value: str) -> Enum:
for m in cls:
if m.display() == value:
return m


class ModelType(BaseDisplayEnum):
TOP_DOWN = "Multi Instance Top Down"
BOTTOM_UP = "Multi Instance Bottom Up"
SINGLE_INSTANCE = "Single Instance"


class TrackerType(BaseDisplayEnum):
SIMPLE = "Simple", "simple"
FLOW = "Flow Shift", "flow"
KALMAN = "Kalman Filter", "kalman"


class Verbosity(BaseDisplayEnum):
JSON = "Json", "json"
RICH = "Rich", "rich"
NONE = "None", "none"
44 changes: 44 additions & 0 deletions sleap/gui/activities/inference/model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from typing import Optional, List

import attr

from sleap.gui.activities.inference.enums import ModelType, TrackerType, Verbosity


@attr.s(auto_attribs=True)
class TrainedModels(object):
model_type: Optional[ModelType] = ModelType.TOP_DOWN
single_instance_model_path: Optional[str] = None
bottom_up_model_path: Optional[str] = None
centroid_model_path: Optional[str] = None
centered_instance_model_path: Optional[str] = None


@attr.s(auto_attribs=True)
class Videos(object):
paths: List[str] = []
frames: List[str] = []


@attr.s(auto_attribs=True)
class Instances(object):
max_num_instances: int = 2
enable_tracking: bool = False
tracking_method: TrackerType = TrackerType.SIMPLE
tracking_window: int = 5


@attr.s(auto_attribs=True)
class Output(object):
output_dir_path: Optional[str] = None
output_file_suffix: Optional[str] = ".predictions.slp"
include_empty_frames: bool = False
verbosity: Verbosity = Verbosity.JSON


@attr.s(auto_attribs=True)
class InferenceGuiModel(object):
models: TrainedModels = TrainedModels()
videos: Videos = Videos()
instances: Instances = Instances()
output: Output = Output()
Loading
0