Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Dropdown List and Implement Mask Copying in Front-End #43

Closed
wants to merge 4 commits into from
Closed
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: 4 additions & 4 deletions src/client/dcp_client/config.cfg
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"server":{
"user": "ubuntu",
"host": "jusuf-vm2",
"data-path": "/home/ubuntu/dcp-data",
"ip": "134.94.88.74",
"user": "local",
"host": "local",
"data-path": "None",
"ip": "localhost",
"port": 7010
}
}
2 changes: 1 addition & 1 deletion src/client/dcp_client/gui/main_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
class MainWindow(QWidget):
'''Main Window Widget object.
Opens the main window of the app where selected images in both directories are listed.
User can view the images, train the mdoel to get the labels, and visualise the result.
User can view the images, train the model to get the labels, and visualise the result.
:param eval_data_path: Chosen path to images without labeles, selected by the user in the WelcomeWindow
:type eval_data_path: string
:param train_data_path: Chosen path to images with labeles, selected by the user in the WelcomeWindow
Expand Down
103 changes: 94 additions & 9 deletions src/client/dcp_client/gui/napari_window.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
from __future__ import annotations
from typing import List, TYPE_CHECKING

from PyQt5.QtWidgets import QWidget, QPushButton, QVBoxLayout, QHBoxLayout
import numpy as np

from PyQt5.QtWidgets import QWidget, QPushButton, QVBoxLayout, QHBoxLayout, QComboBox, QLabel, QGridLayout
from PyQt5.QtCore import Qt
import napari
from napari.qt import thread_worker

if TYPE_CHECKING:
from dcp_client.app import Application

from dcp_client.utils import utils


class NapariWindow(QWidget):
'''Napari Window Widget object.
Opens the napari image viewer to view and fix the labeles.
Expand All @@ -26,29 +31,106 @@ def __init__(self, app: Application):
self.app.search_segs()

# Set the viewer

# with thread_worker():
self.viewer = napari.Viewer(show=False)

self.viewer.add_image(img, name=utils.get_path_stem(self.app.cur_selected_img))

for seg_file in self.app.seg_filepaths:
self.viewer.add_labels(self.app.load_image(seg_file), name=utils.get_path_stem(seg_file))

layer = self.viewer.layers[utils.get_path_stem(self.app.seg_filepaths[0])]

self.changed = False

main_window = self.viewer.window._qt_window
layout = QVBoxLayout()
layout.addWidget(main_window)

layout = QGridLayout()
layout.addWidget(main_window, 0, 0, 1, 4)

# set first mask as active by default
self.active_mask_index = 0

if layer.data.shape[0] >= 2:
# User hint
message_label = QLabel('Choose an active mask')
message_label.setAlignment(Qt.AlignRight)
layout.addWidget(message_label, 1, 0)

# Drop list to choose which is an active mask

self.mask_choice_dropdown = QComboBox()
self.mask_choice_dropdown.addItem('Instance Segmentation Mask', userData=0)
self.mask_choice_dropdown.addItem('Labels Mask', userData=1)
layout.addWidget(self.mask_choice_dropdown, 1, 1)



# when user has chosen the mask, we don't want to change it anymore to avoid errors
lock_button = QPushButton("Confirm Final Choice")
lock_button.clicked.connect(self.set_active_mask)

layout.addWidget(lock_button, 1, 2)
layer.mouse_drag_callbacks.append(self.copy_mask_callback)
layer.events.set_data.connect(lambda event: self.copy_mask_callback(layer, event))

buttons_layout = QHBoxLayout()

add_to_inprogress_button = QPushButton('Move to \'Curatation in progress\' folder')
buttons_layout.addWidget(add_to_inprogress_button)
layout.addWidget(add_to_inprogress_button, 2, 0, 1, 2)
add_to_inprogress_button.clicked.connect(self.on_add_to_inprogress_button_clicked)

add_to_curated_button = QPushButton('Move to \'Curated dataset\' folder')
buttons_layout.addWidget(add_to_curated_button)
layout.addWidget(add_to_curated_button, 2, 2, 1, 2)
add_to_curated_button.clicked.connect(self.on_add_to_curated_button_clicked)

layout.addLayout(buttons_layout)

self.setLayout(layout)
self.show()

def set_active_mask(self):
self.mask_choice_dropdown.setDisabled(True)
self.active_mask_index = self.mask_choice_dropdown.currentData()

def on_mask_choice_changed(self, index):
self.active_mask_index = self.mask_choice_dropdown.itemData(index)

def copy_mask_callback(self, layer, event):

source_mask = layer.data

if event.type == "mouse_press":

c, event_x, event_y = event.position
c, event_x, event_y = int(c), int(np.round(event_x)), int(np.round(event_y))

self.event_coords = (c, event_x, event_y)


elif event.type == "set_data":


if self.event_coords is not None:
c, event_x, event_y = self.event_coords

if c == self.active_mask_index:

labels, counts = np.unique(source_mask[c, event_x - 1: event_x + 2, event_y - 1: event_y + 2], return_counts=True)

if labels.size > 0:

idx = np.argmax(counts)
label = labels[idx]

mask_fill = source_mask[c] == label
source_mask[abs(c - 1)][mask_fill] = label

self.changed = True

else:

mask_fill = source_mask[abs(c - 1)] == 0
source_mask[c][mask_fill] = 0



def on_add_to_curated_button_clicked(self):
'''
Expand All @@ -66,6 +148,7 @@ def on_add_to_curated_button_clicked(self):
message_text = "Please select the segmenation you wish to save from the layer list"
utils.create_warning_box(message_text, message_title="Warning")
return

seg = self.viewer.layers[cur_seg_selected].data

# Move original image
Expand All @@ -78,6 +161,7 @@ def on_add_to_curated_button_clicked(self):
self.app.delete_images(self.app.seg_filepaths)
# TODO Create the Archive folder for the rest? Or move them as well?

self.viewer.close()
self.close()

def on_add_to_inprogress_button_clicked(self):
Expand Down Expand Up @@ -105,4 +189,5 @@ def on_add_to_inprogress_button_clicked(self):
seg = self.viewer.layers[cur_seg_selected].data
self.app.save_image(self.app.inprogr_data_path, cur_seg_selected+'.tiff', seg)

self.viewer.close()
self.close()
2 changes: 0 additions & 2 deletions src/server/dcp_server/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,6 @@ def train(self, imgs, masks):
pred_masks = [self.eval(img) for img in masks]
print(len(pred_masks))
self.metric = np.mean(aggregated_jaccard_index(masks, pred_masks))
# pred_masks = [self.eval(img) for img in masks]

# self.loss = self.loss_fn(masks, pred_masks)

def masks_to_outlines(self, mask):
Expand Down
1 change: 1 addition & 0 deletions src/server/dcp_server/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ def get_centered_patches(img,
if mask_class is not None:
# get the class instance for the specific object
instance_labels.append(l)

class_l = int(np.unique(mask_class[mask[:,:,0]==l]))
#-1 because labels from mask start from 1, we want classes to start from 0
class_labels.append(class_l-1)
Expand Down