diff --git a/cellpose/gui/gui.py b/cellpose/gui/gui.py
index ca66a3eb..ca6bd7e0 100644
--- a/cellpose/gui/gui.py
+++ b/cellpose/gui/gui.py
@@ -9,6 +9,7 @@
from qtpy.QtWidgets import QScrollArea, QMainWindow, QApplication, QWidget, QScrollBar, QComboBox, QGridLayout, QPushButton, QFrame, QCheckBox, QLabel, QProgressBar, QLineEdit, QMessageBox, QGroupBox, QColorDialog
import pyqtgraph as pg
from qtpy.QtGui import QIcon, QColor
+from PIL import Image
import numpy as np
from scipy.stats import mode
@@ -144,6 +145,17 @@ def make_cmap(cm=0):
cmap = pg.ColorMap(pos=np.linspace(0.0, 255, 256), color=color)
return cmap
+def rgb_to_hex(rgb_tuple):
+ """
+ Converts an RGB tuple to a hex color string.
+
+ Args:
+ rgb_tuple (tuple): The RGB tuple (e.g., (255, 0, 0) for red).
+
+ Returns:
+ str: The hex color string (e.g., '#ff0000' for red).
+ """
+ return '#{:02x}{:02x}{:02x}'.format(rgb_tuple[0], rgb_tuple[1], rgb_tuple[2])
def run(image=None):
from ..io import logger_setup
@@ -360,6 +372,15 @@ def minimap_window(self):
self.minimap_window_instance = None
def color_initialization(self):
+ """
+ Initializes the color stack for multi-channel images.
+
+ This method assigns initial colors to each layer in the grayscale image stack.
+ It uses a predefined list of colors and cycles through them if there are more layers
+ than colors available.
+
+ The colors are assigned in a cyclic manner to ensure that each layer has a color.
+ """
colors = [
(255, 0, 0), # Red
(0, 255, 0), # Green
@@ -507,6 +528,8 @@ def onViewChanged(self):
# if an exception of any kind occurs, the specific exception is printed to the console
print(f"An error occurred while changing the view: {e}")
+
+
def make_buttons(self):
self.boldfont = QtGui.QFont("Arial", 11, QtGui.QFont.Bold)
self.boldmedfont = QtGui.QFont("Arial", 9, QtGui.QFont.Bold)
@@ -562,41 +585,45 @@ def make_buttons(self):
self.autobtn.setChecked(True)
self.satBoxG.addWidget(self.autobtn, b0, 1, 1, 8)
-
+ #--- Initialization of Non-Tiff cases ---#
c = 0 # position of the elements in the right side menu
- self.sliders = []
- # ---Create a list (extendable) of color/on-off buttons ---#
+ # Define color names and labels for non-TIFF images
+ colornames = ["red", "Chartreuse", "DodgerBlue"]
+ names = ["red", "green", "blue"]
colors = ["red", "green", "blue"]
- self.marker_buttons = [self.create_color_button(color) for color in colors]
- self.on_off_buttons = [self.create_on_off_button() for color in colors]
+ # Initialize sliders list
+ self.sliders = []
+
+ # Add labels and sliders for non-TIFF images
for r in range(3):
c += 1
- label = QLabel(f'Marker {r + 1}') # create a label for each marker
- color_button = self.marker_buttons[r] # get the corresponding color button
- self.marker_buttons = [self.create_color_button(color) for color in colors]
- on_off_button = self.on_off_buttons[r] # get the corresponding on-off button
- label.setStyleSheet("color: white")
+ # Create a label for each color channel
+ if r == 0:
+ label = QLabel('gray/
red')
+ else:
+ label = QLabel(names[r] + ":")
+ label.setStyleSheet(f"color: {colornames[r]}")
label.setFont(self.boldmedfont)
- self.rightBoxLayout.addWidget(label, c, 0, 1, 1)
- self.rightBoxLayout.addWidget(color_button, c, 9, 1, 1) # add the color button to the layout
- self.rightBoxLayout.addWidget(on_off_button, c, 10, 1, 1) # add the on-off button to the layout
- self.sliders.append(Slider(self, colors[r], None))
- self.sliders[-1].setMinimum(-.1)
- self.sliders[-1].setMaximum(255.1)
- self.sliders[-1].setValue([0, 255])
- self.sliders[-1].setToolTip(
- "NOTE: manually changing the saturation bars does not affect normalization in segmentation"
- )
-
- self.sliders[-1].setFixedWidth(250)
- self.rightBoxLayout.addWidget(self.sliders[-1], c, 2, 1, 7)
- stretch_widget = QWidget()
- self.rightBoxLayout.addWidget(stretch_widget)
-
-
+ self.rightBoxLayout.addWidget(label, c, 0, 1, 2)
+
+ # Create and configure the slider
+ slider = Slider(self, colors[r], None)
+ slider.setMinimum(-.1)
+ slider.setMaximum(255.1)
+ slider.setValue([0, 255])
+ slider.setToolTip("NOTE: manually changing the saturation bars does not affect normalization in segmentation")
+ slider.setFixedWidth(250)
+
+ # Add the slider to the layout
+ self.sliders.append(slider)
+ self.rightBoxLayout.addWidget(slider, c, 2, 1, 7)
+
+ # Add a stretch widget to the layout
+ stretch_widget = QWidget()
+ self.rightBoxLayout.addWidget(stretch_widget)
b += 1
self.drawBox = QGroupBox("Drawing")
self.drawBox.setFont(self.boldfont)
@@ -1050,24 +1077,106 @@ def make_buttons(self):
return b
- def create_color_button(self, color):
+
+ def generate_multi_channel_ui(self, num_layers, is_tiff):
"""
- Creates and initializes all the buttons and UI elements used in the GUI.
- This includes buttons for changing views, drawing, segmentation, model
- selection, and image restoration. Also initializes color buttons with
- specific colors (red, green, blue) and on-off buttons.
+ Generates UI components for multi-channel images.
- Returns:
- int: The number of buttons and UI elements created.
- """
+ This method creates and initializes sliders, color buttons, and on/off buttons based on the number of layers
+ in the loaded multi-channel TIFF image. It also ensures that these components are displayed or hidden appropriately.
+
+ Args:
+ num_layers (int): The number of layers in the loaded multi-channel TIFF image.
+ is_tiff (bool): Flag indicating whether the image is a TIFF file.
+ """
+ # Position of elements in the layout
+ c = 0
+
+ # Clear existing elements in the layout
+ for i in reversed(range(self.rightBoxLayout.count())):
+ widget = self.rightBoxLayout.itemAt(i).widget()
+ if widget is not None:
+ widget.setParent(None)
+
+ # Initialize lists for buttons and sliders
+ self.sliders = []
+ self.marker_buttons = []
+ self.on_off_buttons = []
+
+ if is_tiff:
+ # Create color buttons and on/off buttons for each layer
+ self.marker_buttons = [
+ self.create_color_button(rgb_to_hex(color))
+ for color in self.colors_stack[:num_layers]
+ ]
+ self.on_off_buttons = [self.create_on_off_button() for _ in range(num_layers)]
+
+ for r in range(num_layers):
+ c += 1
+
+ # Create label for each marker
+ label = QLabel(f'Marker {r + 1}')
+ label.setStyleSheet("color: white")
+ label.setFont(self.boldmedfont)
+
+ # Get the corresponding color button and on/off button
+ color_button = self.marker_buttons[r]
+ on_off_button = self.on_off_buttons[r]
+
+ # Add components to the layout
+ self.rightBoxLayout.addWidget(label, c, 0, 1, 1)
+ self.rightBoxLayout.addWidget(color_button, c, 9, 1, 1)
+ self.rightBoxLayout.addWidget(on_off_button, c, 10, 1, 1)
+
+ # Create and add the slider
+ slider_name = r
+ slider_color = rgb_to_hex(self.colors_stack[r])
+ slider = Slider(self, slider_name, slider_color)
+ slider.setMinimum(-.1)
+ slider.setMaximum(255.1)
+ slider.setValue([0, 255])
+ slider.setToolTip("NOTE: manually changing the saturation bars does not affect normalization in segmentation")
+ slider.setFixedWidth(250)
+
+ self.sliders.append(slider)
+ self.rightBoxLayout.addWidget(slider, c, 2, 1, 7)
+
+
+ # Add a stretch widget to the layout
+ stretch_widget = QWidget()
+ self.rightBoxLayout.addWidget(stretch_widget)
+
+ # Show or hide color dialog and on/off buttons based on the is_tiff flag
+ if is_tiff and num_layers > 3:
+ for button in self.marker_buttons:
+ button.show()
+ for button in self.on_off_buttons:
+ button.show()
+ else:
+ for button in self.marker_buttons:
+ button.hide()
+ for button in self.on_off_buttons:
+ button.hide()
+
+ def create_color_button(self, hex_color):
+ """
+ Creates a color button with the specified color.
+
+ Args:
+ hex_color (str): Hex color string.
+
+ Returns:
+ QPushButton: The created color button.
+ """
color_button = QPushButton()
- color_button.setStyleSheet(self.get_color_button_style(color))
+ color_button.setStyleSheet(self.get_color_button_style(hex_color))
color_button.clicked.connect(self.open_color_dialog)
+ print(f"Created button with color: {hex_color}")
return color_button
def create_on_off_button(self):
"""
- Creates a new QPushButton for toggling on and off, with an initial "off" state,
+ Creates a new QPushButton for toggling on and off, with an initial "on" state,
and connects its clicked signal to the toggle_on_off method.
Returns:
@@ -1075,8 +1184,8 @@ def create_on_off_button(self):
"""
on_off_button = QPushButton()
on_off_button.setCheckable(True)
- on_off_button.setChecked(False)
- on_off_button.setIcon(QIcon("cellpose/resources/icon/visibility_off.png")) # Icon for "off" state
+ on_off_button.setChecked(True) # Initial state is "on"
+ on_off_button.setIcon(QIcon("cellpose/resources/icon/visibility_on.png")) # Icon for "on" state
on_off_button.setIconSize(QtCore.QSize(12, 12))
on_off_button.clicked.connect(self.toggle_on_off)
return on_off_button
@@ -1112,19 +1221,20 @@ def open_color_dialog(self):
if color.isValid():
self.sender().setStyleSheet(self.get_color_button_style(color.name()))
- def get_color_button_style(self, color_name):
+ def get_color_button_style(self, hex_color):
"""
- Returns a string with the CSS style for a QPushButton with the specified background color, a solid border, a border width of 1 pixel, and a size of 12x12 pixels.
+ Returns a string with the CSS style for a QPushButton with the specified background color,
+ a solid border, a border width of 1 pixel, and a size of 12x12 pixels.
Args:
- color_name (str): The name of the color to use for the button's background.
+ hex_color (str): The hex color string to use for the button's background.
Returns:
str: The CSS style for the button.
"""
return f"""
QPushButton {{
- background-color: {color_name};
+ background-color: {hex_color};
border-style: solid;
border-width: 1px;
height: 12px;
@@ -1132,6 +1242,7 @@ def get_color_button_style(self, color_name):
}}
"""
+
def level_change(self, r):
r = ["red", "green", "blue"].index(r)
if self.loaded:
diff --git a/cellpose/gui/io.py b/cellpose/gui/io.py
index dc589e25..d3dd314d 100644
--- a/cellpose/gui/io.py
+++ b/cellpose/gui/io.py
@@ -10,7 +10,7 @@
import tifffile
import logging
import fastremap
-
+from PIL import Image
from ..io import imread, imsave, outlines_to_text, add_model, remove_model, save_rois, save_settings, save_features_csv
from ..models import normalize_default, MODEL_DIR, MODEL_LIST_PATH, get_user_models
@@ -110,11 +110,13 @@ def _load_image(parent, filename=None, load_seg=True, load_3D=False):
if filename is None:
name = QFileDialog.getOpenFileName(parent, "Load image")
filename = name[0]
+ print("keine tiff")
#checks if the file is a tiff
if filename and (filename.endswith('.tif') or filename.endswith('.tiff')):
successful_import, grayscale_image_stack = initialize_tiff_images(
filename)
+ print(successful_import)
if successful_import:
parent.grayscale_image_stack = grayscale_image_stack
@@ -122,9 +124,11 @@ def _load_image(parent, filename=None, load_seg=True, load_3D=False):
parent.color_initialization()
parent.generate_color_image_stack()
- # Initialize the Buttons and sliders
- # parent.generate_multi_channel_ui()
-
+ # Initialize the Buttons and sliders for multi-layer TIFF
+ num_layers = len(grayscale_image_stack)
+ is_tiff = True
+ parent.generate_multi_channel_ui(num_layers, is_tiff)
+ print(f"GUI_INFO: Tiff loaded with {len(grayscale_image_stack)} layers")
manual_file = os.path.splitext(filename)[0] + "_seg.npy"
load_mask = False
@@ -798,4 +802,5 @@ def initialize_tiff_images(tiff_file_path):
return ret, processed_images
return ret, []
except Exception as e:
+ print(e)
return False, []