Source code for sleap.gui.widgets.imagedir
"""
Qt widget for showing images from a directory.
There's a seekbar to move between images, and an optional drop-down menu
to select different filename filters (e.g., "validation.*.png").
The typical use-case is to show predictions for each training epoch on the
training/validation images. For this use-case, there's a factory method which
creates widgets with relevant filters from a given training run path.
"""
from sleap import Video
from sleap.gui.widgets.video import QtVideoPlayer
from PySide2 import QtWidgets
import glob
import os
from typing import List, Optional, Text, Tuple
[docs]class QtImageDirectoryWidget(QtVideoPlayer):
"""
Qt widget (window) for showing images from directory with seekbar.
Call `poll()` method to check directory for new files.
Arguments:
directory: The path for which to search for image files.
filters: Filename filters, given as (display name, filter)-tuples,
e.g., ("Validation", "validation.*.png")
"""
def __init__(
self, directory: Text, filters: Optional[List[Tuple[Text, Text]]] = None
):
self.directory = directory
self.filters = filters
self.files = []
super(QtImageDirectoryWidget, self).__init__()
self.seekbar.tick_index_offset = 0 # show frame numbers indexed by 0
self.changedPlot.connect(
lambda vp, idx, select_idx: self.setWindowTitle(
self._get_win_title_for_frame(idx)
)
)
self.resize(360, 400)
if self.filters:
self.filter_menu = QtWidgets.QComboBox()
self.filter_menu.addItems([filter[0] for filter in self.filters])
self.filter_menu.currentIndexChanged.connect(self.setFilter)
self.layout.addWidget(self.filter_menu)
self.setFilter(self.filter_menu.currentIndex())
else:
self.poll()
def _get_win_title_for_frame(self, frame_idx: int) -> Text:
"""Get window title to use based on specified frame."""
return (
os.path.basename(self.files[frame_idx])
if frame_idx < len(self.files)
else ""
)
[docs] def setFilter(self, filter_idx: int):
"""Set filter (by number) for which files in directory to show."""
self.filter_idx = filter_idx
self.poll()
@property
def _current_filter_mask(self) -> Text:
if not self.filters:
return "*"
return self.filters[self.filter_idx][1]
[docs] def poll(self):
"""Re-scans directory (using current filter) and updates widget."""
path = os.path.join(self.directory, self._current_filter_mask)
print(f"Polling: {path}")
files = glob.glob(path)
files.sort()
if not files:
return
if files != self.files:
was_on_last_image = False
if self.video is None:
was_on_last_image = True
self.show()
elif self.state["frame_idx"] == self.video.last_frame_idx:
was_on_last_image = True
self.files = files
self.video = Video.from_image_filenames(filenames=files)
self.load_video(video=self.video)
if was_on_last_image:
self.state["frame_idx"] = self.video.last_frame_idx
elif self.state["frame_idx"]:
self.state["frame_idx"] = min(
self.state["frame_idx"], self.video.last_frame_idx
)
[docs] @classmethod
def make_training_vizualizer(cls, run_path: Text) -> "QtImageDirectoryWidget":
"""
Factory method for only currently use-case of this widget.
Args:
run_path: The run path directory for model, should contain viz
subdirectory.
Returns:
Instance of `QtImageDirectoryWidget` widget.
"""
dir = os.path.join(run_path, "viz")
filters = [("Validation", "validation.*.png"), ("Training", "train.*.png")]
win = QtImageDirectoryWidget(dir, filters=filters)
return win
if __name__ == "__main__":
from PySide2.QtWidgets import QApplication
app = QApplication([])
# run_path = "tests/data/json_format_v1/models/200420_100503.centroid.70"
# window = QtImageDirectoryWidget.make_training_vizualizer(run_path)
window = QtImageDirectoryWidget(
directory="tests/data/videos/",
filters=[("JPEG", "*.jpg"), ("Robot 1", "*1.jpg")],
)
window.show()
window.plot()
app.exec_()