Skip to content

Commit

Permalink
Update to Python 3.10 (#1989)
Browse files Browse the repository at this point in the history
* Bump version to 1.4.1a3

* Trigger workflows

* Force full viewport rendering

* Remove env flag workaround for Qt bug

* Fix threading errors

* More threading cleanup

* networkx backward compatibility

* Remove rendering mode hack

* Disable albumentations update check.

* Add safer thread/timer stopping

* networkx backwards compatibility

* networkx backwards compatibility

* Update tf.data APIs

* Use legacy optimizer on Apple Silicon

* Lazy loading for OpenCV

* Fix backwards compatibility with tensorflow

* Lint

* More lint

---------

Co-authored-by: Talmo Pereira <[email protected]>
  • Loading branch information
roomrys and talmo authored Dec 17, 2024
1 parent c5e3ce8 commit 53ce002
Show file tree
Hide file tree
Showing 42 changed files with 149 additions and 132 deletions.
2 changes: 2 additions & 0 deletions .conda_mac/meta.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ requirements:
- conda-forge::qudida
- conda-forge::albumentations >=1.4.15 # Handle Nan keypoints
- conda-forge::ndx-pose <0.2.0
- conda-forge::lazy_loader

run:
- conda-forge::python >=3.10.0,<3.11.0
Expand Down Expand Up @@ -89,3 +90,4 @@ requirements:
- conda-forge::qudida
- conda-forge::albumentations >=1.4.15 # Handle Nan keypoints
- conda-forge::ndx-pose <0.2.0
- conda-forge::lazy_loader
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Please include information about how you installed.
- OS:
<!-- [e.g. ubuntu 20.04, macOS 11.0] -->
- Version(s):
<!-- e.g. [SLEAP v1.4.1a2, python 3.8] --->
<!-- e.g. [SLEAP v1.4.1a3, python 3.8] --->
- SLEAP installation method (listed [here](https://sleap.ai/installation.html#)):
- [ ] [Conda from package](https://sleap.ai/installation.html#conda-package)
- [ ] [Conda from source](https://sleap.ai/installation.html#conda-from-source)
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/build_conda_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ on:
- "requirements.txt"
- "dev_requirements.txt"
- "environment_build.yml"
- ".github/workflows/build_conda_ci.yml"
- ".github/workflows/build_conda_ci.yml" # Run

# If RUN_BUILD_JOB is set to true, then RUN_ID will be overwritten to the current run id
env:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/build_pypi_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ on:
- "jupyter_requirements.txt"
- "pypi_requirements.txt"
- "environment_build.yml"
- ".github/workflows/build_pypi_ci.yml"
- ".github/workflows/build_pypi_ci.yml" # Run

jobs:
build:
Expand Down
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', all others to 'sleap.ai/develop'
- main
- develop
- liezl/remove-python-version-from-website-build-yml
- liezl/bump-to-1.4.1a3-py310
paths:
- "docs/**"
- "README.rst"
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,5 @@ venv.bak/

# OS generated files
.DS_Store

models/
4 changes: 2 additions & 2 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,15 @@
copyright = f"2019–{date.today().year}, Talmo Lab"

# The short X.Y version
version = "1.4.1a2"
version = "1.4.1a3"

# Get the sleap version
# with open("../sleap/version.py") as f:
# version_file = f.read()
# version = re.search("\d.+(?=['\"])", version_file).group(0)

# Release should be the full branch name
release = "v1.4.1a2"
release = "v1.4.1a3"

html_title = f"SLEAP ({release})"
html_short_title = "SLEAP"
Expand Down
10 changes: 5 additions & 5 deletions docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ SLEAP can be installed as a Python package on Windows, Linux, and Mac OS. For qu
````{tabs}
```{group-tab} Windows and Linux
```bash
conda create -y -n sleap -c conda-forge -c nvidia -c sleap -c anaconda sleap=1.4.1a2
conda create -y -n sleap -c conda-forge -c nvidia -c sleap -c anaconda sleap=1.4.1a3
```
```
```{group-tab} Mac OS
```bash
conda create -y -n sleap -c conda-forge -c anaconda -c sleap sleap=1.4.1a2
conda create -y -n sleap -c conda-forge -c anaconda -c sleap sleap=1.4.1a3
```
```
````
Expand Down Expand Up @@ -147,7 +147,7 @@ SLEAP can be installed three different ways: via {ref}`conda package<condapackag
````{tabs}
```{group-tab} Windows and Linux
```bash
conda create -y -n sleap -c conda-forge -c nvidia -c sleap -c anaconda sleap=1.4.1a2
conda create -y -n sleap -c conda-forge -c nvidia -c sleap -c anaconda sleap=1.4.1a3
```
```{note}
- This comes with CUDA to enable GPU support. All you need is to have an NVIDIA GPU and [updated drivers](https://nvidia.com/drivers).
Expand All @@ -157,7 +157,7 @@ SLEAP can be installed three different ways: via {ref}`conda package<condapackag
```
```{group-tab} Mac OS
```bash
conda create -y -n sleap -c conda-forge -c anaconda -c sleap sleap=1.4.1a2
conda create -y -n sleap -c conda-forge -c anaconda -c sleap sleap=1.4.1a3
```
```{note}
This will also work in CPU mode if you don't have a GPU on your machine.
Expand Down Expand Up @@ -240,7 +240,7 @@ SLEAP can be installed three different ways: via {ref}`conda package<condapackag
```
3. Finally, we can perform the `pip install`:
```bash
pip install sleap[pypi]==1.4.1a2
pip install sleap[pypi]==1.4.1a3
```
```{note}
The pypi distributed package of SLEAP ships with the following extras:
Expand Down
2 changes: 1 addition & 1 deletion environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ channels:
- conda-forge
- nvidia
- sleap
- anaconda

dependencies:
# Packages SLEAP uses directly
Expand Down Expand Up @@ -42,6 +41,7 @@ dependencies:
- conda-forge::qudida
- conda-forge::albumentations >=1.4.15 # Handle Nan keypoints
- conda-forge::ndx-pose <0.2.0
- conda-forge::lazy_loader

# Packages required by tensorflow to find/use GPUs
- conda-forge::cudatoolkit ==11.3.1
Expand Down
3 changes: 2 additions & 1 deletion environment_mac.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,6 @@ dependencies:
- conda-forge::qudida
- conda-forge::albumentations >=1.4.15 # Handle Nan keypoints
- conda-forge::ndx-pose <0.2.0
- conda-forge::lazy_loader
- pip:
- "--editable=.[conda_dev]"
- "--editable=.[conda_dev]"
2 changes: 1 addition & 1 deletion environment_no_cuda.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,6 @@ dependencies:
- conda-forge::qudida
- conda-forge::albumentations >=1.4.15 # Handle Nan keypoints
- conda-forge::ndx-pose <0.2.0

- conda-forge::lazy_loader
- pip:
- "--editable=.[conda_dev]"
10 changes: 6 additions & 4 deletions pypi_requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,15 @@ scipy>=1.7.0
scikit-image
scikit-learn>=1.0.0
seaborn
tensorflow==2.9.2; platform_machine != 'arm64'
tensorflow-hub
albumentations>=1.4.15 # Handle Nan keypoints
albumentations
ndx-pose<0.2.0
lazy-loader
tensorflow==2.9.2; platform_machine != 'arm64'
tensorflow-hub; platform_machine != 'arm64'
# Silicon Mac specific packages
tensorflow-hub >=0.14.0,<=0.16.1; sys_platform == 'darwin' and platform_machine == 'arm64'
tensorflow-macos >=2.10.0,<2.13.0; sys_platform == 'darwin' and platform_machine == 'arm64'
tensorflow-metal; sys_platform == 'darwin' and platform_machine == 'arm64'
tensorflow-metal >=0.8.0,<=1.1.0; sys_platform == 'darwin' and platform_machine == 'arm64'

# Dependencies of dependencies
# tensorboard 2.11.2 has requirement protobuf<4,>=3.9.2
Expand Down
7 changes: 6 additions & 1 deletion sleap/__init__.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
import logging
import sys
import os

# Disable albumentations update check before imports.
os.environ["NO_ALBUMENTATIONS_UPDATE"] = "1"

# Setup logging to stdout
logging.basicConfig(stream=sys.stdout, level=logging.INFO)

# Import submodules we want available at top-level
from sleap.version import __version__, versions
from sleap.io.dataset import Labels, load_file

from sleap.io.video import Video, load_video
from sleap.instance import LabeledFrame, Instance, PredictedInstance, Track
from sleap.skeleton import Skeleton
from sleap.io.dataset import Labels, load_file

import sleap.nn
from sleap.nn.data import pipelines
from sleap.nn import inference
Expand Down
12 changes: 9 additions & 3 deletions sleap/gui/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,9 +230,11 @@ def closeEvent(self, event):
# Save preferences.
prefs.save()

will_accept = False

if not self.state["has_changes"]:
# No unsaved changes, so accept event (close)
event.accept()
will_accept = True
else:
msgBox = QMessageBox()
msgBox.setText("Do you want to save the changes to this project?")
Expand All @@ -249,12 +251,16 @@ def closeEvent(self, event):
event.ignore()
elif ret_val == QMessageBox.Discard:
# don't save, just close
event.accept()
will_accept = True
elif ret_val == QMessageBox.Save:
# save
self.commands.saveProject()
# accept event (closes window)
event.accept()
will_accept = True

if will_accept:
self.player.cleanup()
event.accept()

def dragEnterEvent(self, event):
# TODO: Parse filenames and accept only if valid ext (or folder)
Expand Down
4 changes: 1 addition & 3 deletions sleap/gui/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ class which inherits from `AppCommand` (or a more specialized class such as
from typing import Callable, Dict, Iterator, List, Optional, Tuple, Type, Union, cast

import attr
import cv2
import numpy as np
from qtpy import QtCore, QtGui, QtWidgets

Expand Down Expand Up @@ -1794,8 +1793,7 @@ def ask(context: CommandContext, params: dict) -> bool:
"""Shows gui for replacing videos in project."""

def _get_truncation_message(truncation_messages, path, video):
reader = cv2.VideoCapture(path)
last_vid_frame = int(reader.get(cv2.CAP_PROP_FRAME_COUNT))
last_vid_frame = len(Video.from_filename(path))
lfs: List[LabeledFrame] = list(context.labels.get(video))
if lfs is not None:
lfs.sort(key=lambda lf: lf.frame_idx)
Expand Down
4 changes: 3 additions & 1 deletion sleap/gui/dialogs/importvideos.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@

import h5py
import qimage2ndarray
import cv2
import lazy_loader

cv2 = lazy_loader.load("cv2")

from typing import Any, Dict, List, Optional

Expand Down
4 changes: 0 additions & 4 deletions sleap/gui/dialogs/missingfiles.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
"""
Gui for prompting the user to locate one or more missing files.
"""

import os

from pathlib import Path, PurePath
from typing import Callable, List

from qtpy import QtWidgets, QtCore, QtGui

from sleap.io import pathutils
from sleap.gui.dialogs.filedialog import FileDialog

Expand Down
38 changes: 30 additions & 8 deletions sleap/gui/widgets/video.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,11 +116,21 @@ def __init__(self, *args, **kwargs):
# event to event queue from the request handler.
self.process.connect(self.doProcessing)

# Defer timer creation to worker thread construction time
self.timer = None

@QtCore.Slot()
def start_timers(self):
# Start timer which will trigger processing events every 20 ms when we're free
self.timer = QtCore.QTimer()
self.timer = QtCore.QTimer(self)
self.timer.timeout.connect(self.doProcessing)
self.timer.start(20)

@QtCore.Slot()
def stop_timers(self):
if self.timer:
self.timer.stop()

def doProcessing(self):
self._last_process_time = time.time()

Expand Down Expand Up @@ -211,7 +221,7 @@ def __init__(
*args,
**kwargs,
):
super(QtVideoPlayer, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)

self.setAcceptDrops(True)

Expand Down Expand Up @@ -257,11 +267,12 @@ def __init__(
self._loader_thread = QtCore.QThread()
self._video_image_loader = LoadImageWorker()
self._video_image_loader.moveToThread(self._loader_thread)
self._loader_thread.started.connect(self._video_image_loader.start_timers)
self._loader_thread.start()

# Connect signal so that image will be shown after it's loaded
self._video_image_loader.result.connect(
lambda qimage: self.view.setImage(qimage)
self.on_new_frame, QtCore.Qt.QueuedConnection
)

def update_selection_state(a, b):
Expand All @@ -282,16 +293,27 @@ def update_selection_state(a, b):

self.view.show()

# Call cleanup method when application exits to end worker thread
self.destroyed.connect(self.cleanup)
atexit.register(self.cleanup)
# Call cleanup method when application exits to end worker thread.
# Note: This is commented out in favor of the MainWindow.closeEvent() path.
# self.destroyed.connect(self.cleanup)
# app = QApplication.instance()
# if app:
# app.aboutToQuit.connect(self.cleanup)

if video is not None:
self.load_video(video)

def on_new_frame(self, qimage):
self.view.setImage(qimage)

def cleanup(self):
self._loader_thread.quit()
self._loader_thread.wait()
if self._loader_thread.isRunning():
QtCore.QMetaObject.invokeMethod(
self._video_image_loader, "stop_timers", QtCore.Qt.QueuedConnection
)
QtWidgets.QApplication.processEvents()
self._loader_thread.quit()
self._loader_thread.wait()

def dragEnterEvent(self, event):
if self.parentWidget():
Expand Down
5 changes: 3 additions & 2 deletions sleap/info/feature_suggestions.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@
from time import time
from typing import Dict, List, Optional, Tuple

import cv2

from sklearn.decomposition import PCA
from sklearn.cluster import KMeans

Expand All @@ -22,6 +20,9 @@
from skimage.util.shape import view_as_windows

from sleap.io.video import Video
import lazy_loader

cv2 = lazy_loader.load("cv2")

logger = logging.getLogger(__name__)

Expand Down
5 changes: 3 additions & 2 deletions sleap/io/dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,13 @@
make_instance_cattr,
PredictedInstance,
)

from sleap.io import pathutils
from sleap.io.video import Video, ImgStoreVideo, HDF5Video
from sleap.gui.suggestions import SuggestionFrame
from sleap.gui.dialogs.missingfiles import MissingFilesDialog
from sleap.rangelist import RangeList
from sleap.util import uniquify, json_dumps


"""
The version number to put in the Labels JSON format.
"""
Expand Down Expand Up @@ -2657,6 +2656,8 @@ def video_callback(
context["changed_on_load"] = True

if use_gui:
from sleap.gui.dialogs.missingfiles import MissingFilesDialog

# If there are still missing paths, prompt user
if sum(missing):
# If we are using dummy for any video not found by user
Expand Down
Loading

0 comments on commit 53ce002

Please sign in to comment.