Skip to content

Commit

Permalink
Add Skeleton Templates (#1122)
Browse files Browse the repository at this point in the history
  • Loading branch information
aaprasad authored Feb 23, 2023
1 parent 57ee08a commit 8696aa5
Show file tree
Hide file tree
Showing 23 changed files with 915 additions and 131 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ jobs:
if: matrix.os == 'windows-2019'
shell: bash -l {0}
run: |
pytest
pytest --durations=-1
- name: Test with pytest (Ubuntu)
if: matrix.os == 'ubuntu-20.04'
shell:
Expand All @@ -111,7 +111,7 @@ jobs:
sudo apt install xvfb libxkbcommon-x11-0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-xinerama0 libxcb-xfixes0
sudo Xvfb :1 -screen 0 1024x768x24 </dev/null &
export DISPLAY=":1"
xvfb-run pytest --cov=sleap --cov-report=xml tests/
xvfb-run pytest --cov=sleap --cov-report=xml --durations=-1 tests/
- name: Upload coverage
uses: codecov/codecov-action@v1
if: matrix.os == 'ubuntu-20.04'
Expand Down
113 changes: 99 additions & 14 deletions sleap/gui/app.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
"""
Main GUI application for labeling, training/inference, and proofreading.
"""Main GUI application for labeling, training/inference, and proofreading.
Each open project is an instance of :py:class:`MainWindow`.
Expand Down Expand Up @@ -50,7 +49,6 @@
import os
import random
import platform
import requests
from pathlib import Path

from typing import Callable, List, Optional, Tuple
Expand All @@ -59,7 +57,7 @@
from qtpy.QtCore import Qt, QEvent

from qtpy.QtWidgets import QApplication, QMainWindow, QWidget, QDockWidget
from qtpy.QtWidgets import QVBoxLayout, QHBoxLayout, QGroupBox
from qtpy.QtWidgets import QVBoxLayout, QHBoxLayout, QGroupBox, QTabWidget
from qtpy.QtWidgets import QLabel, QPushButton, QComboBox
from qtpy.QtWidgets import QMessageBox

Expand All @@ -70,6 +68,7 @@
from sleap.io.dataset import Labels
from sleap.info.summary import StatisticSeries
from sleap.gui.commands import CommandContext, UpdateTopic
from sleap.gui.widgets.views import CollapsibleWidget
from sleap.gui.widgets.video import QtVideoPlayer
from sleap.gui.widgets.slider import set_slider_marks_from_labels
from sleap.gui.dataviews import (
Expand All @@ -81,7 +80,12 @@
LabeledFrameTableModel,
SkeletonNodeModel,
)
from sleap.util import parse_uri_path
from sleap.util import (
parse_uri_path,
decode_preview_image,
get_package_file,
find_files_by_suffix,
)

from sleap.gui.dialogs.filedialog import FileDialog
from sleap.gui.dialogs.formbuilder import YamlFormWidget, FormBuilderModalDialog
Expand Down Expand Up @@ -161,6 +165,8 @@ def __init__(
self.state["propagate track labels"] = prefs["propagate track labels"]
self.state["node label size"] = prefs["node label size"]
self.state["share usage data"] = prefs["share usage data"]
self.state["skeleton_preview_image"] = None
self.state["skeleton_description"] = "No skeleton loaded yet"
if no_usage_data:
self.state["share usage data"] = False
self.state.connect("marker size", self.plotFrame)
Expand Down Expand Up @@ -1006,8 +1012,78 @@ def _add_button(to, label, action, key=None):
"Skeleton", tab_with=videos_layout.parent().parent()
)

gb = QGroupBox("Nodes")
gb = CollapsibleWidget("Templates")
vb = QVBoxLayout()
hb = QHBoxLayout()

skeletons_folder = get_package_file("sleap/skeletons")
skeletons_json_files = find_files_by_suffix(
skeletons_folder, suffix=".json", depth=1
)
skeletons_names = [json.name.split(".")[0] for json in skeletons_json_files]
self.skeletonTemplates = QComboBox()
self.skeletonTemplates.addItems(skeletons_names)
self.skeletonTemplates.setEditable(False)
hb.addWidget(self.skeletonTemplates)
_add_button(hb, "Load", self.commands.openSkeletonTemplate)
hbw = QWidget()
hbw.setLayout(hb)
vb.addWidget(hbw)

hb = QHBoxLayout()
self.skeleton_preview_image = QLabel("Preview Skeleton")
hb.addWidget(self.skeleton_preview_image)
hb.setAlignment(self.skeleton_preview_image, Qt.AlignLeft)

self.skeleton_description = QLabel(
f'<strong>Description:</strong> {self.state["skeleton_description"]}'
)
self.skeleton_description.setWordWrap(True)
hb.addWidget(self.skeleton_description)
hb.setAlignment(self.skeleton_description, Qt.AlignLeft)

hbw = QWidget()
hbw.setLayout(hb)
vb.addWidget(hbw)

def updatePreviewImage(preview_image_bytes: bytes):

# Decode the preview image
preview_image = decode_preview_image(preview_image_bytes)

# Create a QImage from the Image
preview_image = QtGui.QImage(
preview_image.tobytes(),
preview_image.size[0],
preview_image.size[1],
QtGui.QImage.Format_RGBA8888, # Format for RGBA images (see Image.mode)
)

preview_image = QtGui.QPixmap.fromImage(preview_image)

self.skeleton_preview_image.setPixmap(preview_image)

gb.set_content_layout(vb)
skeleton_layout.addWidget(gb)

def update_skeleton_preview(idx: int):
skel = Skeleton.load_json(skeletons_json_files[idx])
self.state["skeleton_description"] = (
f"<strong>Description:</strong> {skel.description}<br><br>"
f"<strong>Nodes ({len(skel)}):</strong> {', '.join(skel.node_names)}"
)
self.skeleton_description.setText(self.state["skeleton_description"])
updatePreviewImage(skel.preview_image)

self.skeletonTemplates.currentIndexChanged.connect(update_skeleton_preview)
update_skeleton_preview(idx=0)

gb = QGroupBox("Project Skeleton")
vgb = QVBoxLayout()

nodes_widget = QWidget()
vb = QVBoxLayout()
graph_tabs = QTabWidget()
self.skeletonNodesTable = GenericTableView(
state=self.state,
row_name="node",
Expand All @@ -1023,13 +1099,13 @@ def _add_button(to, label, action, key=None):
hbw = QWidget()
hbw.setLayout(hb)
vb.addWidget(hbw)
gb.setLayout(vb)
skeleton_layout.addWidget(gb)
nodes_widget.setLayout(vb)
graph_tabs.addTab(nodes_widget, "Nodes")

def _update_edge_src():
self.skeletonEdgesDst.model().skeleton = self.state["skeleton"]

gb = QGroupBox("Edges")
edges_widget = QWidget()
vb = QVBoxLayout()
self.skeletonEdgesTable = GenericTableView(
state=self.state,
Expand Down Expand Up @@ -1066,16 +1142,21 @@ def new_edge():
hbw = QWidget()
hbw.setLayout(hb)
vb.addWidget(hbw)
gb.setLayout(vb)
skeleton_layout.addWidget(gb)
edges_widget.setLayout(vb)
graph_tabs.addTab(edges_widget, "Edges")
vgb.addWidget(graph_tabs)

hb = QHBoxLayout()
_add_button(hb, "Load Skeleton", self.commands.openSkeleton)
_add_button(hb, "Save Skeleton", self.commands.saveSkeleton)
_add_button(hb, "Load From File...", self.commands.openSkeleton)
_add_button(hb, "Save As...", self.commands.saveSkeleton)

hbw = QWidget()
hbw.setLayout(hb)
skeleton_layout.addWidget(hbw)
vgb.addWidget(hbw)

# Add graph tabs to "Project Skeleton" group box
gb.setLayout(vgb)
skeleton_layout.addWidget(gb)

####### Suggestions #######
suggestions_layout = _make_dock(
Expand Down Expand Up @@ -1836,3 +1917,7 @@ def main(args: Optional[list] = None):
app.exec_()

pass


if __name__ == "__main__":
main()
Loading

0 comments on commit 8696aa5

Please sign in to comment.