Skip to content

Commit

Permalink
Merge pull request #140 from AllenCell/creation-ui-functionality
Browse files Browse the repository at this point in the history
curation demo ready
  • Loading branch information
yrkim98 authored Oct 10, 2023
2 parents 9a1ebe4 + 46f4e67 commit d7b6ba2
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 5 deletions.
9 changes: 9 additions & 0 deletions src/allencell_ml_segmenter/curation/curation_data_class.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from dataclasses import dataclass
from pathlib import Path


@dataclass
class CurationRecord:
raw_file: Path
seg1: Path
to_use: bool
19 changes: 17 additions & 2 deletions src/allencell_ml_segmenter/curation/curation_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from allencell_ml_segmenter.curation.main_view import CurationMainView

import napari
from napari.utils.notifications import show_info

from allencell_ml_segmenter.main.main_model import MainModel

Expand Down Expand Up @@ -41,16 +42,30 @@ def __init__(self, viewer: napari.Viewer, main_model: MainModel):
self.curation_input_view = CurationInputView(self.curation_model)
self.initialize_view(self.curation_input_view)

self.curation_main_view = CurationMainView()
self.curation_main_view = CurationMainView(
self.viewer, self.curation_model
)
self.initialize_view(self.curation_main_view)

self.set_view(self.curation_input_view)
self.curation_model.subscribe(
Event.PROCESS_CURATION_INPUT_STARTED,
self,
lambda x: self.set_view(self.curation_main_view),
lambda x: self.go_to_main_view(self.curation_main_view),
)

def go_to_main_view(self, view: View) -> None:
if (
self.curation_model.get_raw_directory() is not None
and self.curation_model.get_raw_channel() is not None
and self.curation_model.get_seg1_directory() is not None
and self.curation_model.get_seg1_channel() is not None
):
self.set_view(view)
self.curation_main_view.curation_setup()
else:
_ = show_info("Please select all required fields")

def set_view(self, view: View) -> None:
"""
Set the current views, must be initialized first
Expand Down
26 changes: 24 additions & 2 deletions src/allencell_ml_segmenter/curation/input_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ def __init__(self, curation_model: CurationModel):
QLabel("Image channel"), 1, 1, alignment=Qt.AlignRight
)
self._raw_image_channel_combo: QComboBox = QComboBox()
self._raw_image_channel_combo.activated.connect(
self.raw_channel_selected
)
raw_grid_layout.addWidget(
self._raw_image_channel_combo, 1, 2, alignment=Qt.AlignLeft
)
Expand All @@ -91,7 +94,7 @@ def __init__(self, curation_model: CurationModel):
# TODO update model accordingly
self._seg1_directory_select: InputButton = InputButton(
self._curation_model,
lambda dir: self._curation_model.set_raw_directory(dir),
lambda dir: self._curation_model.set_seg1_directory(dir),
"Select directory...",
FileInputMode.DIRECTORY,
)
Expand All @@ -102,6 +105,9 @@ def __init__(self, curation_model: CurationModel):
QLabel("Image channel"), 1, 1, alignment=Qt.AlignRight
)
self._seg1_image_channel_combo: QComboBox = QComboBox()
self._seg1_image_channel_combo.activated.connect(
self.seg1_channel_selected
)
seg1_grid_layout.addWidget(
self._seg1_image_channel_combo, 1, 2, alignment=Qt.AlignLeft
)
Expand All @@ -122,7 +128,7 @@ def __init__(self, curation_model: CurationModel):
# TODO update model accordingly
self._seg2_directory_select: InputButton = InputButton(
self._curation_model,
lambda dir: self._curation_model.set_raw_directory(dir),
lambda dir: self._curation_model.set_seg2_directory(dir),
"Select directory...",
FileInputMode.DIRECTORY,
)
Expand All @@ -133,6 +139,9 @@ def __init__(self, curation_model: CurationModel):
QLabel("Image channel"), 1, 1, alignment=Qt.AlignRight
)
self._seg2_image_channel_combo: QComboBox = QComboBox()
self._seg2_image_channel_combo.activated.connect(
self.seg2_channel_selected
)
seg2_grid_layout.addWidget(
self._seg2_image_channel_combo, 1, 2, alignment=Qt.AlignLeft
)
Expand Down Expand Up @@ -178,7 +187,9 @@ def update_raw_channels(self, event):
)
]
)
# default first index
self._raw_image_channel_combo.setCurrentIndex(0)
self._curation_model.set_raw_channel(0)

def update_seg1_channels(self, event):
self._seg1_image_channel_combo.clear()
Expand All @@ -191,6 +202,7 @@ def update_seg1_channels(self, event):
]
)
self._seg1_image_channel_combo.setCurrentIndex(0)
self._curation_model.set_seg1_channel(0)

def update_seg2_channels(self, event):
self._seg2_image_channel_combo.clear()
Expand All @@ -203,3 +215,13 @@ def update_seg2_channels(self, event):
]
)
self._seg2_image_channel_combo.setCurrentIndex(0)
self._curation_model.set_seg2_channel(0)

def raw_channel_selected(self, index):
self._curation_model.set_raw_channel(index)

def seg1_channel_selected(self, index):
self._curation_model.set_seg1_channel(index)

def seg2_channel_selected(self, index):
self._curation_model.set_seg2_channel(index)
95 changes: 94 additions & 1 deletion src/allencell_ml_segmenter/curation/main_view.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import List
import napari
from qtpy.QtCore import Qt
from qtpy.QtWidgets import (
QVBoxLayout,
Expand All @@ -8,20 +10,31 @@
QPushButton,
QProgressBar,
QRadioButton,
QGridLayout,
)
from aicsimageio import AICSImage

from allencell_ml_segmenter._style import Style
from allencell_ml_segmenter.core.view import View
from allencell_ml_segmenter.curation.curation_data_class import CurationRecord
from allencell_ml_segmenter.curation.curation_model import CurationModel
from allencell_ml_segmenter.main.main_model import MainModel
from allencell_ml_segmenter.widgets.label_with_hint_widget import LabelWithHint

from napari.utils.notifications import show_info


class CurationMainView(View):
"""
View for Curation UI
"""

def __init__(self):
def __init__(self, viewer: napari.Viewer, curation_model: CurationModel):
super().__init__()
self.viewer = viewer
self._curation_model = curation_model
self.curation_index = 0
self.curation_record: List[CurationRecord] = list()

self.setLayout(QVBoxLayout())
self.layout().setContentsMargins(0, 10, 0, 10)
Expand Down Expand Up @@ -65,6 +78,7 @@ def __init__(self):
progress_bar_layout.addWidget(inner_progress_frame)
self.next_button: QPushButton = QPushButton("Next ►")
self.next_button.setObjectName("big_blue_btn")
self.next_button.clicked.connect(self.next_image)
progress_bar_layout.addWidget(
self.next_button, alignment=Qt.AlignRight
)
Expand All @@ -80,6 +94,7 @@ def __init__(self):
QLabel("Use this image for training")
)
self.yes_radio: QRadioButton = QRadioButton("Yes")
self.yes_radio.setChecked(True)
use_image_frame.layout().addWidget(self.yes_radio)
self.no_radio: QRadioButton = QRadioButton("No")
use_image_frame.layout().addWidget(self.no_radio)
Expand All @@ -93,10 +108,12 @@ def __init__(self):
QLabel("File name..."), alignment=Qt.AlignLeft
)
self.layout().addLayout(excluding_mask_labels)

# buttons for excluding mask
excluding_mask_buttons = QHBoxLayout()
excluding_create_button = QPushButton("+ Create")
excluding_create_button.setObjectName("small_blue_btn")
excluding_create_button.clicked.connect(self.add_points_in_viewer)
excluding_propagate_button = QPushButton("Propagate in 3D")
excluding_delete_button = QPushButton("Delete")
excluding_save_button = QPushButton("Save")
Expand Down Expand Up @@ -133,3 +150,79 @@ def getTypeOfWork(self):

def showResults(self):
print("show result")

def remove_all_images(self):
self.viewer.layers.clear()

def curation_setup(self):
_ = show_info("Loading curation images")
self.raw_images = [
f for f in self._curation_model.get_raw_directory().iterdir()
]
self.seg1_images = [
f for f in self._curation_model.get_seg1_directory().iterdir()
]

# set progress bar
self.progress_bar.setMaximum(len(self.raw_images))
self.progress_bar.setValue(1)
# set progress bar hint
self.progress_bar_image_count.setText(
f"{self.curation_index + 1}/{len(self.raw_images)}"
)
self.remove_all_images()

first_raw = self.raw_images[0]
self.viewer.add_image(
AICSImage(str(first_raw)).data, name=f"[raw] {first_raw.name}"
)
first_seg1 = self.seg1_images[0]
self.viewer.add_image(
AICSImage(str(first_seg1)).data, name=f"[seg] {first_seg1.name}"
)

def next_image(self):
_ = show_info("Loading the next image...")
self.curation_index = self.curation_index + 1
if self.curation_index < len(self.raw_images):
self.remove_all_images()

raw_to_view = self.raw_images[self.curation_index]
self.viewer.add_image(
AICSImage(str(raw_to_view)).data,
name=f"[raw] {raw_to_view.name}",
)
seg1_to_view = self.seg1_images[self.curation_index]
self.viewer.add_image(
AICSImage(str(seg1_to_view)).data,
name=f"[seg] {seg1_to_view.name}",
)
self._update_progress_bar()
else:
_ = show_info("No more image to load")

def _update_progress_bar(self):
# update progress bar
self.progress_bar.setValue(self.progress_bar.value() + 1)
# set progress bar hint
self.progress_bar_image_count.setText(
f"{self.curation_index + 1}/{len(self.raw_images)}"
)

def _update_curation_record(self):
use_this_image = True
if self.no_radio.isChecked():
use_this_image = False
if self._curation_model.get_seg2_directory() is not None:
self.curation_record.append(
CurationRecord(
self.raw_images[self.curation_index],
self.seg1_images[self.curation_index],
use_this_image,
)
)

def add_points_in_viewer(self):
_ = show_info("Draw excluding area")
points_layer = self.viewer.add_shapes(None)
points_layer.mode = "add_polygon"

0 comments on commit d7b6ba2

Please sign in to comment.