Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 3 additions & 5 deletions .github/workflows/gpu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,10 @@
# run: |
# pip install torch==1.11.0+cu115 torchvision==0.12.0+cu115 -f https://download.pytorch.org/whl/torch_stable.html
#
# - name: Install package and test dependencies
# - name: Install dependencies
# run: |
# python -m pip install --upgrade "pip<24.0"
# pip install -r requirements.txt
# pip install pytest pytest-cov
# python setup.py develop
# python -m pip install --upgrade pip
# pip install -e .
#
# - name: Setup test data
# run: |
Expand Down
8 changes: 3 additions & 5 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,10 @@ jobs:
run: |
pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu

- name: Install package and test dependencies
- name: Install dependencies
run: |
python -m pip install --upgrade "pip<24.0"
pip install -r requirements.txt
pip install pytest pytest-cov
python setup.py develop
python -m pip install --upgrade pip
pip install .

- name: Setup test data
run: |
Expand Down
18 changes: 14 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,19 @@ learning research into action detection:
## Installation
For full installation instructions, see [this readme file](docs/installation.md).

In brief:
* [Install PyTorch](https://pytorch.org/)
* `pip install deepethogram`
### Quick Install (Recommended)
We recommend using [UV](https://docs.astral.sh/uv/) for fast, reliable Python package management:

1. Install UV:
```bash
curl -LsSf https://astral.sh/uv/install.sh | sh
```
For troubleshooting or Windows, see [other installation methods](https://docs.astral.sh/uv/getting-started/installation/)

2. Install DeepEthogram:
```bash
uv pip install deepethogram
```

## Data
**NEW!** All datasets collected and annotated by the DeepEthogram authors are now available from this DropBox link:
Expand Down Expand Up @@ -65,7 +75,7 @@ The major dependencies for DeepEthogram are as follows:
* pytorch, torchvision: all the neural networks, training, and inference pipelines were written in PyTorch
* pytorch-lightning: for nice model training base classes
* kornia: for GPU-based image augmentations
* pyside2: for the GUI
* PySide6: for the GUI (upgraded from PySide2 in v0.3.0)
* opencv: for video and image reading and writing
* opencv_transforms: for fast image augmentation
* scikit-learn, scipy: for binary classification metrics
Expand Down
18 changes: 9 additions & 9 deletions deepethogram/gui/custom_widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
from typing import Union

import numpy as np
from PySide2 import QtCore, QtGui, QtWidgets
from PySide2.QtCore import Signal, Slot
from PySide6 import QtCore, QtGui, QtWidgets
from PySide6.QtCore import Signal, Slot

from deepethogram.file_io import VideoReader

Expand All @@ -19,7 +19,7 @@


def numpy_to_qpixmap(image: np.ndarray) -> QtGui.QPixmap:
if image.dtype == np.float:
if image.dtype == np.float64 or image.dtype == np.float32:
image = float_to_uint8(image)
H, W, C = int(image.shape[0]), int(image.shape[1]), int(image.shape[2])
if C == 4:
Expand All @@ -33,7 +33,7 @@ def numpy_to_qpixmap(image: np.ndarray) -> QtGui.QPixmap:


def float_to_uint8(image: np.ndarray) -> np.ndarray:
if image.dtype == np.float:
if image.dtype == np.float64 or image.dtype == np.float32:
image = (image * 255).clip(min=0, max=255).astype(np.uint8)
return image

Expand Down Expand Up @@ -650,7 +650,7 @@ def initialize(
button = self._make_button(behavior, i)
self.buttons.append(button)
layout.addWidget(button, 0, alignment=QtCore.Qt.AlignTop)
layout.setMargin(0)
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(0)
self.layout = layout
self.setLayout(self.layout)
Expand Down Expand Up @@ -788,7 +788,7 @@ def add_behavior(self, behavior: str):

self.label._add_row()
if i < 10:
self.toggle_shortcuts.append(QtWidgets.QShortcut(QtGui.QKeySequence(str(i)), self))
self.toggle_shortcuts.append(QtGui.QShortcut(QtGui.QKeySequence(str(i)), self))
self.toggle_shortcuts[i].activated.connect(self.buttons.buttons[i].click)


Expand Down Expand Up @@ -875,10 +875,10 @@ def __init__(self):
fixed=False,
)

next_shortcut = QtWidgets.QShortcut(QtGui.QKeySequence("Right"), self)
next_shortcut = QtGui.QShortcut(QtGui.QKeySequence("Right"), self)
next_shortcut.activated.connect(partial(self.label.label.change_view_dx, 1))
# next_shortcut.activated.connect(partial(self.label.change_view_dx, 1))
back_shortcut = QtWidgets.QShortcut(QtGui.QKeySequence("Left"), self)
back_shortcut = QtGui.QShortcut(QtGui.QKeySequence("Left"), self)
back_shortcut.activated.connect(partial(self.label.label.change_view_dx, -1))

self.setCentralWidget(self.label)
Expand All @@ -897,4 +897,4 @@ def sizeHint(self):
testing.initialize(behaviors=["background", "a", "b", "c", "d", "e"], n_timepoints=15000, debug=True)
testing.update()
testing.show()
app.exec_()
app.exec()
34 changes: 17 additions & 17 deletions deepethogram/gui/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
import numpy as np
import pandas as pd
from omegaconf import DictConfig, OmegaConf
from PySide2 import QtCore, QtGui, QtWidgets
from PySide2.QtCore import Slot
from PySide2.QtWidgets import QFileDialog, QInputDialog, QMainWindow
from PySide6 import QtCore, QtGui, QtWidgets
from PySide6.QtCore import Slot
from PySide6.QtWidgets import QFileDialog, QInputDialog, QMainWindow

from deepethogram import configuration, projects, utils
from deepethogram.file_io import VideoReader
Expand Down Expand Up @@ -63,37 +63,37 @@ def __init__(self, cfg: DictConfig):

# scroll down to Standard Shorcuts to find what the keys are called:
# https://doc.qt.io/qt-5/qkeysequence.html
next_shortcut = QtWidgets.QShortcut(QtGui.QKeySequence("Right"), self)
next_shortcut = QtGui.QShortcut(QtGui.QKeySequence("Right"), self)
# partial functions create a new, separate function with certain default arguments
next_shortcut.activated.connect(partial(self.move_n_frames, 1))
next_shortcut.activated.connect(self.user_did_something)

up_shortcut = QtWidgets.QShortcut(QtGui.QKeySequence("Up"), self)
up_shortcut = QtGui.QShortcut(QtGui.QKeySequence("Up"), self)
up_shortcut.activated.connect(partial(self.move_n_frames, -cfg.vertical_arrow_jump))
up_shortcut.activated.connect(self.user_did_something)

down_shortcut = QtWidgets.QShortcut(QtGui.QKeySequence("Down"), self)
down_shortcut = QtGui.QShortcut(QtGui.QKeySequence("Down"), self)
down_shortcut.activated.connect(partial(self.move_n_frames, cfg.vertical_arrow_jump))
down_shortcut.activated.connect(self.user_did_something)

back_shortcut = QtWidgets.QShortcut(QtGui.QKeySequence("Left"), self)
back_shortcut = QtGui.QShortcut(QtGui.QKeySequence("Left"), self)
back_shortcut.activated.connect(partial(self.move_n_frames, -1))
back_shortcut.activated.connect(self.user_did_something)

jumpleft_shortcut = QtWidgets.QShortcut(QtGui.QKeySequence("Ctrl+Left"), self)
jumpleft_shortcut = QtGui.QShortcut(QtGui.QKeySequence("Ctrl+Left"), self)
jumpleft_shortcut.activated.connect(partial(self.move_n_frames, -cfg.control_arrow_jump))
jumpleft_shortcut.activated.connect(self.user_did_something)

jumpright_shortcut = QtWidgets.QShortcut(QtGui.QKeySequence("Ctrl+Right"), self)
jumpright_shortcut = QtGui.QShortcut(QtGui.QKeySequence("Ctrl+Right"), self)
jumpright_shortcut.activated.connect(partial(self.move_n_frames, cfg.control_arrow_jump))
jumpright_shortcut.activated.connect(self.user_did_something)

self.ui.actionSave_Project.triggered.connect(self.save)
save_shortcut = QtWidgets.QShortcut(QtGui.QKeySequence("Ctrl+S"), self)
save_shortcut = QtGui.QShortcut(QtGui.QKeySequence("Ctrl+S"), self)
save_shortcut.activated.connect(self.save)
finalize_shortcut = QtWidgets.QShortcut(QtGui.QKeySequence("Ctrl+F"), self)
finalize_shortcut = QtGui.QShortcut(QtGui.QKeySequence("Ctrl+F"), self)
finalize_shortcut.activated.connect(self.finalize)
open_shortcut = QtWidgets.QShortcut(QtGui.QKeySequence("Ctrl+O"), self)
open_shortcut = QtGui.QShortcut(QtGui.QKeySequence("Ctrl+O"), self)
open_shortcut.activated.connect(self.load_project)
self.ui.finalize_labels.clicked.connect(self.finalize)
self.ui.exportPredictions.clicked.connect(self.export_predictions)
Expand All @@ -109,7 +109,7 @@ def __init__(self, cfg: DictConfig):
self.latent_name = None
self.thresholds = None
for i in range(10):
self.toggle_shortcuts.append(QtWidgets.QShortcut(QtGui.QKeySequence(str(i)), self))
self.toggle_shortcuts.append(QtGui.QShortcut(QtGui.QKeySequence(str(i)), self))
tmp_func = partial(self.respond_to_keypress, i)
self.toggle_shortcuts[i].activated.connect(tmp_func)
self.toggle_shortcuts[i].activated.connect(self.user_did_something)
Expand Down Expand Up @@ -394,7 +394,7 @@ def generate_featureextractor_inference_args(self):
keys.append(key)
no_outputs.append(record["output"] is None)
form = ShouldRunInference(keys, no_outputs)
ret = form.exec_()
ret = form.exec()
if not ret:
return
should_infer = form.get_outputs()
Expand Down Expand Up @@ -518,7 +518,7 @@ def generate_sequence_inference_args(self):
if has_latents[i]:
keys_with_features.append(key)
form = ShouldRunInference(keys_with_features, no_sequence_outputs)
ret = form.exec_()
ret = form.exec()
if not ret:
return
should_infer = form.get_outputs()
Expand Down Expand Up @@ -617,7 +617,7 @@ def run_overnight(self):

def _new_project(self):
form = CreateProject()
ret = form.exec_()
ret = form.exec()
if not ret:
return
project_name = form.project_box.text()
Expand Down Expand Up @@ -1183,7 +1183,7 @@ def run() -> None:
window.resize(1024, 768)
window.show()

sys.exit(app.exec_())
sys.exit(app.exec())


def entry() -> None:
Expand Down
32 changes: 16 additions & 16 deletions deepethogram/gui/mainwindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
#
# WARNING! All changes made in this file will be lost!

from PySide2 import QtCore, QtWidgets
from PySide6 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
Expand Down Expand Up @@ -186,41 +186,41 @@ def setupUi(self, MainWindow):
self.statusBar = QtWidgets.QStatusBar(MainWindow)
self.statusBar.setObjectName("statusBar")
MainWindow.setStatusBar(self.statusBar)
self.actionNew_Project = QtWidgets.QAction(MainWindow)
self.actionNew_Project = QtGui.QAction(MainWindow)
self.actionNew_Project.setObjectName("actionNew_Project")
self.actionSave_Project = QtWidgets.QAction(MainWindow)
self.actionSave_Project = QtGui.QAction(MainWindow)
self.actionSave_Project.setEnabled(False)
self.actionSave_Project.setObjectName("actionSave_Project")
self.actionAdd = QtWidgets.QAction(MainWindow)
self.actionAdd = QtGui.QAction(MainWindow)
self.actionAdd.setEnabled(False)
self.actionAdd.setObjectName("actionAdd")
self.actionRemove = QtWidgets.QAction(MainWindow)
self.actionRemove = QtGui.QAction(MainWindow)
self.actionRemove.setEnabled(False)
self.actionRemove.setObjectName("actionRemove")
self.actionStyle = QtWidgets.QAction(MainWindow)
self.actionStyle = QtGui.QAction(MainWindow)
self.actionStyle.setObjectName("actionStyle")
self.actionOpen = QtWidgets.QAction(MainWindow)
self.actionOpen = QtGui.QAction(MainWindow)
self.actionOpen.setEnabled(False)
self.actionOpen.setObjectName("actionOpen")
self.actionEdit_list = QtWidgets.QAction(MainWindow)
self.actionEdit_list = QtGui.QAction(MainWindow)
self.actionEdit_list.setObjectName("actionEdit_list")
self.actionNext = QtWidgets.QAction(MainWindow)
self.actionNext = QtGui.QAction(MainWindow)
self.actionNext.setObjectName("actionNext")
self.actionPrevious = QtWidgets.QAction(MainWindow)
self.actionPrevious = QtGui.QAction(MainWindow)
self.actionPrevious.setObjectName("actionPrevious")
self.actionOpen_Project = QtWidgets.QAction(MainWindow)
self.actionOpen_Project = QtGui.QAction(MainWindow)
self.actionOpen_Project.setObjectName("actionOpen_Project")
self.importLabels = QtWidgets.QAction(MainWindow)
self.importLabels = QtGui.QAction(MainWindow)
self.importLabels.setObjectName("importLabels")
self.actionAdd_videos = QtWidgets.QAction(MainWindow)
self.actionAdd_videos = QtGui.QAction(MainWindow)
self.actionAdd_videos.setObjectName("actionAdd_videos")
self.classifierInference = QtWidgets.QAction(MainWindow)
self.classifierInference = QtGui.QAction(MainWindow)
self.classifierInference.setCheckable(True)
self.classifierInference.setObjectName("classifierInference")
self.actionOvernight = QtWidgets.QAction(MainWindow)
self.actionOvernight = QtGui.QAction(MainWindow)
self.actionOvernight.setCheckable(True)
self.actionOvernight.setObjectName("actionOvernight")
self.actionAdd_multiple = QtWidgets.QAction(MainWindow)
self.actionAdd_multiple = QtGui.QAction(MainWindow)
self.actionAdd_multiple.setObjectName("actionAdd_multiple")
self.menuDeepEthogram.addAction(self.actionNew_Project)
self.menuDeepEthogram.addAction(self.actionSave_Project)
Expand Down
10 changes: 5 additions & 5 deletions deepethogram/gui/menus_and_popups.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import pathlib
import warnings

from PySide2 import QtCore, QtWidgets
from PySide6 import QtCore, QtWidgets


def simple_popup_question(parent, message: str):
Expand All @@ -25,7 +25,7 @@ def overwrite_or_not(parent):
)
overwrite = msgBox.addButton("Overwrite", QtWidgets.QMessageBox.YesRole)
unlabeled = msgBox.addButton("Only import unlabeled", QtWidgets.QMessageBox.NoRole)
msgBox.exec_()
msgBox.exec()
if msgBox.clickedButton() is overwrite:
return True
elif msgBox.clickedButton() is unlabeled:
Expand All @@ -46,7 +46,7 @@ def __init__(self, parent=None):
)
msgBox.addButton(QtWidgets.QPushButton("Overwrite"), QtWidgets.QMessageBox.YesRole)
msgBox.addButton(QtWidgets.QPushButton("Only import unlabeled"), QtWidgets.QMessageBox.NoRole)
msgBox.exec_()
msgBox.exec()


class CreateProject(QtWidgets.QDialog):
Expand Down Expand Up @@ -148,8 +148,8 @@ def get_outputs(self):
form = ShouldRunInference(
["M134_20141203_v001", "M134_20141203_v002", "M134_20141203_v004"] * num, [True, True, False] * num
)
ret = form.exec_()
ret = form.exec()
if ret:
print(form.get_outputs())
# ret = app.exec_()
# ret = app.exec()
# print(ret)
9 changes: 5 additions & 4 deletions docker/Dockerfile-full
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,11 @@ RUN curl -sLo ~/miniconda.sh https://repo.continuum.io/miniconda/Miniconda3-py39

# install
RUN conda install python=3.7 -y
RUN pip install setuptools --upgrade && pip install --upgrade "pip<24.0"
RUN pip install torch==1.11.0+cu115 torchvision==0.12.0+cu115 -f https://download.pytorch.org/whl/torch_stable.html

RUN pip install --upgrade pip
# Install deepethogram with dev dependencies
ADD . /app/deepethogram
WORKDIR /app/deepethogram
# Use Python 3.7 compatible pyproject.toml
RUN cp pyproject_py37.toml pyproject.toml
ENV DEG_VERSION='full'
RUN pip install -e .
RUN pip install -e ".[dev]"
11 changes: 7 additions & 4 deletions docker/Dockerfile-gui
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,16 @@ RUN curl -sLo ~/miniconda.sh https://repo.continuum.io/miniconda/Miniconda3-py39

# install
RUN conda install python=3.7 -y
RUN pip install setuptools --upgrade && pip install --upgrade pip
RUN pip install --upgrade pip

# TODO: REFACTOR CODE SO IT'S POSSIBLE TO RUN GUI WITHOUT TORCH
RUN conda install pytorch cpuonly -c pytorch
# Install PyTorch CPU via pip to avoid conda issues with Python 3.7
RUN pip install torch==1.11.0+cpu torchvision==0.12.0+cpu -f https://download.pytorch.org/whl/torch_stable.html

# # needed for pandas for some reason
# Install deepethogram with dev dependencies
ADD . /app/deepethogram
WORKDIR /app/deepethogram
# Use Python 3.7 compatible pyproject.toml
RUN cp pyproject_py37.toml pyproject.toml
ENV DEG_VERSION='gui'
RUN pip install -e .
RUN pip install -e ".[dev]"
10 changes: 6 additions & 4 deletions docker/Dockerfile-headless
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,13 @@ RUN curl -sLo ~/miniconda.sh https://repo.continuum.io/miniconda/Miniconda3-py39

# install
RUN conda install python=3.7 -y
RUN pip install setuptools --upgrade && pip install --upgrade pip
RUN conda install pytorch torchvision torchaudio cudatoolkit=11.3 -c pytorch
RUN pip install --upgrade pip
# RUN conda install pytorch torchvision torchaudio cudatoolkit=11.3 -c pytorch

# # needed for pandas for some reason
# Install deepethogram with dev dependencies
ADD . /app/deepethogram
WORKDIR /app/deepethogram
# Use Python 3.7 compatible pyproject.toml
RUN cp pyproject_py37.toml pyproject.toml
ENV DEG_VERSION='headless'
RUN pip install -e .
RUN pip install -e ".[dev]"
Loading
Loading