From bcb0353a82b6084b8de071d89291177a2434df37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cristi=C3=A1n=20Maureira-Fredes?= Date: Tue, 29 Oct 2019 17:57:07 +0100 Subject: Add external python modules examples These examples will be used at the QtWS2019: - Matplotlib: Widget interacting with a 3D plot - OpenCV: Webcam pattern detection - Scikit Image: Image filters An About Qt section was added to all examples. Change-Id: I14da69c9b5ecdc8409bcdb335135a8b1fa763bb1 Reviewed-by: Friedemann Kleint --- examples/external/matplotlib/requirements.txt | 1 + examples/external/matplotlib/widget_3dplot.py | 241 +++++++++++++++++++++ examples/external/opencv/requirements.txt | 1 + .../external/opencv/webcam_pattern_detection.py | 206 ++++++++++++++++++ examples/external/scikit/requirements.txt | 1 + .../external/scikit/staining_colors_separation.py | 183 ++++++++++++++++ 6 files changed, 633 insertions(+) create mode 100644 examples/external/matplotlib/requirements.txt create mode 100644 examples/external/matplotlib/widget_3dplot.py create mode 100644 examples/external/opencv/requirements.txt create mode 100644 examples/external/opencv/webcam_pattern_detection.py create mode 100644 examples/external/scikit/requirements.txt create mode 100644 examples/external/scikit/staining_colors_separation.py (limited to 'examples') diff --git a/examples/external/matplotlib/requirements.txt b/examples/external/matplotlib/requirements.txt new file mode 100644 index 000000000..6ccafc3f9 --- /dev/null +++ b/examples/external/matplotlib/requirements.txt @@ -0,0 +1 @@ +matplotlib diff --git a/examples/external/matplotlib/widget_3dplot.py b/examples/external/matplotlib/widget_3dplot.py new file mode 100644 index 000000000..8cc5293ed --- /dev/null +++ b/examples/external/matplotlib/widget_3dplot.py @@ -0,0 +1,241 @@ +############################################################################# +## +## Copyright (C) 2019 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the Qt for Python examples of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:LGPL$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU Lesser General Public License Usage +## Alternatively, this file may be used under the terms of the GNU Lesser +## General Public License version 3 as published by the Free Software +## Foundation and appearing in the file LICENSE.LGPL3 included in the +## packaging of this file. Please review the following information to +## ensure the GNU Lesser General Public License version 3 requirements +## will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 2.0 or (at your option) the GNU General +## Public license version 3 or any later version approved by the KDE Free +## Qt Foundation. The licenses are as published by the Free Software +## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-2.0.html and +## https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +import sys + +import numpy as np +from matplotlib.backends.backend_qt5agg import FigureCanvas +from matplotlib.figure import Figure +from mpl_toolkits.mplot3d import axes3d +from PySide2.QtCore import Qt, Slot +from PySide2.QtGui import QKeySequence +from PySide2.QtWidgets import (QAction, QApplication, QComboBox, QHBoxLayout, + QHeaderView, QLabel, QMainWindow, QSlider, + QTableWidget, QTableWidgetItem, QVBoxLayout, + QWidget) + + +"""This example implements the interaction between Qt Widgets and a 3D +matplotlib plot""" + + +class ApplicationWindow(QMainWindow): + def __init__(self, parent=None): + QMainWindow.__init__(self, parent) + + self.column_names = ["Column A", "Column B", "Column C"] + + # Central widget + self._main = QWidget() + self.setCentralWidget(self._main) + + # Main menu bar + self.menu = self.menuBar() + self.menu_file = self.menu.addMenu("File") + exit = QAction("Exit", self, triggered=qApp.quit) + self.menu_file.addAction(exit) + + self.menu_about = self.menu.addMenu("&About") + about = QAction("About Qt", self, shortcut=QKeySequence(QKeySequence.HelpContents), + triggered=qApp.aboutQt) + self.menu_about.addAction(about) + + # Figure (Left) + self.fig = Figure(figsize=(5, 3)) + self.canvas = FigureCanvas(self.fig) + + # Sliders (Left) + self.slider_azim = QSlider(minimum=0, maximum=360, orientation=Qt.Horizontal) + self.slider_elev = QSlider(minimum=0, maximum=360, orientation=Qt.Horizontal) + + self.slider_azim_layout = QHBoxLayout() + self.slider_azim_layout.addWidget(QLabel("{}".format(self.slider_azim.minimum()))) + self.slider_azim_layout.addWidget(self.slider_azim) + self.slider_azim_layout.addWidget(QLabel("{}".format(self.slider_azim.maximum()))) + + self.slider_elev_layout = QHBoxLayout() + self.slider_elev_layout.addWidget(QLabel("{}".format(self.slider_elev.minimum()))) + self.slider_elev_layout.addWidget(self.slider_elev) + self.slider_elev_layout.addWidget(QLabel("{}".format(self.slider_elev.maximum()))) + + # Table (Right) + self.table = QTableWidget() + header = self.table.horizontalHeader() + header.setSectionResizeMode(QHeaderView.Stretch) + + # ComboBox (Right) + self.combo = QComboBox() + self.combo.addItems(["Wired", "Surface", "Triangular Surface", "Sphere"]) + + # Right layout + rlayout = QVBoxLayout() + rlayout.setContentsMargins(1, 1, 1, 1) + rlayout.addWidget(QLabel("Plot type:")) + rlayout.addWidget(self.combo) + rlayout.addWidget(self.table) + + # Left layout + llayout = QVBoxLayout() + rlayout.setContentsMargins(1, 1, 1, 1) + llayout.addWidget(self.canvas, 88) + llayout.addWidget(QLabel("Azimuth:"), 1) + llayout.addLayout(self.slider_azim_layout, 5) + llayout.addWidget(QLabel("Elevation:"), 1) + llayout.addLayout(self.slider_elev_layout, 5) + + # Main layout + layout = QHBoxLayout(self._main) + layout.addLayout(llayout, 70) + layout.addLayout(rlayout, 30) + + # Signal and Slots connections + self.combo.currentTextChanged.connect(self.combo_option) + self.slider_azim.valueChanged.connect(self.rotate_azim) + self.slider_elev.valueChanged.connect(self.rotate_elev) + + # Initial setup + self.plot_wire() + self._ax.view_init(30, 30) + self.slider_azim.setValue(30) + self.slider_elev.setValue(30) + self.fig.canvas.mpl_connect("button_release_event", self.on_click) + + # Matplotlib slot method + def on_click(self, event): + azim, elev = self._ax.azim, self._ax.elev + self.slider_azim.setValue(azim + 180) + self.slider_elev.setValue(elev + 180) + + # Utils methods + + def set_table_data(self, X, Y, Z): + for i in range(len(X)): + self.table.setItem(i, 0, QTableWidgetItem("{:.2f}".format(X[i]))) + self.table.setItem(i, 1, QTableWidgetItem("{:.2f}".format(Y[i]))) + self.table.setItem(i, 2, QTableWidgetItem("{:.2f}".format(Z[i]))) + + def set_canvas_table_configuration(self, row_count, data): + self.fig.set_canvas(self.canvas) + self._ax = self.canvas.figure.add_subplot(projection="3d") + + self._ax.set_xlabel(self.column_names[0]) + self._ax.set_ylabel(self.column_names[1]) + self._ax.set_zlabel(self.column_names[2]) + + self.table.setRowCount(row_count) + self.table.setColumnCount(3) + self.table.setHorizontalHeaderLabels(self.column_names) + self.set_table_data(data[0], data[1], data[2]) + + # Plot methods + + def plot_wire(self): + # Data + self.X, self.Y, self.Z = axes3d.get_test_data(0.03) + + self.set_canvas_table_configuration(len(self.X[0]), (self.X[0], self.Y[0], self.Z[0])) + self._ax.plot_wireframe(self.X, self.Y, self.Z, rstride=10, cstride=10, cmap="viridis") + self.canvas.draw() + + def plot_surface(self): + # Data + self.X, self.Y = np.meshgrid(np.linspace(-6, 6, 30), np.linspace(-6, 6, 30)) + self.Z = np.sin(np.sqrt(self.X ** 2 + self.Y ** 2)) + + self.set_canvas_table_configuration(len(self.X[0]), (self.X[0], self.Y[0], self.Z[0])) + self._ax.plot_surface(self.X, self.Y, self.Z, + rstride=1, cstride=1, cmap="viridis", edgecolor="none") + self.canvas.draw() + + def plot_triangular_surface(self): + # Data + radii = np.linspace(0.125, 1.0, 8) + angles = np.linspace(0, 2 * np.pi, 36, endpoint=False)[..., np.newaxis] + self.X = np.append(0, (radii * np.cos(angles)).flatten()) + self.Y = np.append(0, (radii * np.sin(angles)).flatten()) + self.Z = np.sin(-self.X * self.Y) + + self.set_canvas_table_configuration(len(self.X), (self.X, self.Y, self.Z)) + self._ax.plot_trisurf(self.X, self.Y, self.Z, linewidth=0.2, antialiased=True) + self.canvas.draw() + + def plot_sphere(self): + # Data + u = np.linspace(0, 2 * np.pi, 100) + v = np.linspace(0, np.pi, 100) + self.X = 10 * np.outer(np.cos(u), np.sin(v)) + self.Y = 10 * np.outer(np.sin(u), np.sin(v)) + self.Z = 9 * np.outer(np.ones(np.size(u)), np.cos(v)) + + self.set_canvas_table_configuration(len(self.X), (self.X[0], self.Y[0], self.Z[0])) + self._ax.plot_surface(self.X, self.Y, self.Z) + self.canvas.draw() + + # Slots + + @Slot() + def combo_option(self, text): + if text == "Wired": + self.plot_wire() + elif text == "Surface": + self.plot_surface() + elif text == "Triangular Surface": + self.plot_triangular_surface() + elif text == "Sphere": + self.plot_sphere() + + @Slot() + def rotate_azim(self, value): + self._ax.view_init(self._ax.elev, value) + self.fig.set_canvas(self.canvas) + self.canvas.draw() + + @Slot() + def rotate_elev(self, value): + self._ax.view_init(value, self._ax.azim) + self.fig.set_canvas(self.canvas) + self.canvas.draw() + + +if __name__ == "__main__": + app = QApplication(sys.argv) + w = ApplicationWindow() + w.setFixedSize(1280, 720) + w.show() + app.exec_() diff --git a/examples/external/opencv/requirements.txt b/examples/external/opencv/requirements.txt new file mode 100644 index 000000000..0dd006bbc --- /dev/null +++ b/examples/external/opencv/requirements.txt @@ -0,0 +1 @@ +opencv-python diff --git a/examples/external/opencv/webcam_pattern_detection.py b/examples/external/opencv/webcam_pattern_detection.py new file mode 100644 index 000000000..7c18a9da5 --- /dev/null +++ b/examples/external/opencv/webcam_pattern_detection.py @@ -0,0 +1,206 @@ +############################################################################# +## +## Copyright (C) 2019 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the Qt for Python examples of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:LGPL$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU Lesser General Public License Usage +## Alternatively, this file may be used under the terms of the GNU Lesser +## General Public License version 3 as published by the Free Software +## Foundation and appearing in the file LICENSE.LGPL3 included in the +## packaging of this file. Please review the following information to +## ensure the GNU Lesser General Public License version 3 requirements +## will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 2.0 or (at your option) the GNU General +## Public license version 3 or any later version approved by the KDE Free +## Qt Foundation. The licenses are as published by the Free Software +## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-2.0.html and +## https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +import os +import sys +import time + +import cv2 +from PySide2.QtCore import Qt, QThread, Signal, Slot +from PySide2.QtGui import QImage, QKeySequence, QPixmap +from PySide2.QtWidgets import (QAction, QApplication, QComboBox, QGroupBox, + QHBoxLayout, QLabel, QMainWindow, QPushButton, + QSizePolicy, QVBoxLayout, QWidget) + + +"""This example uses the video from a webcam to apply pattern +detection from the OpenCV module. e.g.: face, eyes, body, etc.""" + + +class Thread(QThread): + updateFrame = Signal(QImage) + + def __init__(self, parent=None): + QThread.__init__(self, parent) + self.trained_file = None + self.status = True + self.cap = True + + def set_file(self, fname): + # The data comes with the 'opencv-python' module + self.trained_file = os.path.join(cv2.data.haarcascades, fname) + + def run(self): + self.cap = cv2.VideoCapture(0) + while self.status: + cascade = cv2.CascadeClassifier(self.trained_file) + ret, frame = self.cap.read() + if not ret: + continue + + # Reading frame in gray scale to process the pattern + gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) + + detections = cascade.detectMultiScale(gray_frame, scaleFactor=1.1, + minNeighbors=5, minSize=(30, 30)) + + # Drawing green rectangle around the pattern + for (x, y, w, h) in detections: + pos_ori = (x, y) + pos_end = (x + w, y + h) + color = (0, 255, 0) + cv2.rectangle(frame, pos_ori, pos_end, color, 2) + + # Reading the image in RGB to display it + color_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) + + # Creating and scaling QImage + h, w, ch = color_frame.shape + img = QImage(color_frame.data, w, h, ch * w, QImage.Format_RGB888) + scaled_img = img.scaled(640, 480, Qt.KeepAspectRatio) + + # Emit signal + self.updateFrame.emit(scaled_img) + sys.exit(-1) + + +class Window(QMainWindow): + def __init__(self): + QMainWindow.__init__(self) + # Title and dimensions + self.setWindowTitle("Patterns detection") + self.setGeometry(0, 0, 800, 500) + + # Main menu bar + self.menu = self.menuBar() + self.menu_file = self.menu.addMenu("File") + exit = QAction("Exit", self, triggered=qApp.quit) + self.menu_file.addAction(exit) + + self.menu_about = self.menu.addMenu("&About") + about = QAction("About Qt", self, shortcut=QKeySequence(QKeySequence.HelpContents), + triggered=qApp.aboutQt) + self.menu_about.addAction(about) + + # Create a label for the display camera + self.label = QLabel(self) + self.label.setFixedSize(640, 480) + + # Thread in charge of updating the image + self.th = Thread(self) + self.th.finished.connect(self.close) + self.th.updateFrame.connect(self.setImage) + + # Model group + self.group_model = QGroupBox("Trained model") + self.group_model.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding) + model_layout = QHBoxLayout() + + self.combobox = QComboBox() + for xml_file in os.listdir(cv2.data.haarcascades): + if xml_file.endswith(".xml"): + self.combobox.addItem(xml_file) + + model_layout.addWidget(QLabel("File:"), 10) + model_layout.addWidget(self.combobox, 90) + self.group_model.setLayout(model_layout) + + # Buttons layout + buttons_layout = QHBoxLayout() + self.button1 = QPushButton("Start") + self.button2 = QPushButton("Stop/Close") + self.button1.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding) + self.button2.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding) + buttons_layout.addWidget(self.button2) + buttons_layout.addWidget(self.button1) + + right_layout = QHBoxLayout() + right_layout.addWidget(self.group_model, 1) + right_layout.addLayout(buttons_layout, 1) + + # Main layout + layout = QVBoxLayout() + layout.addWidget(self.label) + layout.addLayout(right_layout) + + # Central widget + widget = QWidget(self) + widget.setLayout(layout) + self.setCentralWidget(widget) + + # Connections + self.button1.clicked.connect(self.start) + self.button2.clicked.connect(self.kill_thread) + self.button2.setEnabled(False) + self.combobox.currentTextChanged.connect(self.set_model) + + @Slot() + def set_model(self, text): + self.th.set_file(text) + + @Slot() + def kill_thread(self): + print("Finishing...") + self.button2.setEnabled(False) + self.button1.setEnabled(True) + self.th.cap.release() + cv2.destroyAllWindows() + self.status = False + self.th.terminate() + # Give time for the thread to finish + time.sleep(1) + + @Slot() + def start(self): + print("Starting...") + self.button2.setEnabled(True) + self.button1.setEnabled(False) + self.th.set_file(self.combobox.currentText()) + self.th.start() + + @Slot(QImage) + def setImage(self, image): + self.label.setPixmap(QPixmap.fromImage(image)) + + +if __name__ == "__main__": + app = QApplication() + w = Window() + w.show() + sys.exit(app.exec_()) diff --git a/examples/external/scikit/requirements.txt b/examples/external/scikit/requirements.txt new file mode 100644 index 000000000..391ca2f08 --- /dev/null +++ b/examples/external/scikit/requirements.txt @@ -0,0 +1 @@ +scikit-image diff --git a/examples/external/scikit/staining_colors_separation.py b/examples/external/scikit/staining_colors_separation.py new file mode 100644 index 000000000..91de1f63c --- /dev/null +++ b/examples/external/scikit/staining_colors_separation.py @@ -0,0 +1,183 @@ +############################################################################# +## +## Copyright (C) 2019 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the Qt for Python examples of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:LGPL$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU Lesser General Public License Usage +## Alternatively, this file may be used under the terms of the GNU Lesser +## General Public License version 3 as published by the Free Software +## Foundation and appearing in the file LICENSE.LGPL3 included in the +## packaging of this file. Please review the following information to +## ensure the GNU Lesser General Public License version 3 requirements +## will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 2.0 or (at your option) the GNU General +## Public license version 3 or any later version approved by the KDE Free +## Qt Foundation. The licenses are as published by the Free Software +## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-2.0.html and +## https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +import sys + +import numpy as np +from matplotlib.backends.backend_qt5agg import FigureCanvas +from matplotlib.colors import LinearSegmentedColormap +from matplotlib.figure import Figure +from PySide2.QtCore import QSize, Qt, Slot +from PySide2.QtGui import QKeySequence +from PySide2.QtWidgets import (QAction, QApplication, QHBoxLayout, QLabel, + QMainWindow, QPushButton, QSizePolicy, + QVBoxLayout, QWidget) +from skimage import data +from skimage.color import rgb2hed +from skimage.exposure import rescale_intensity + + +class ApplicationWindow(QMainWindow): + """ + Example based on the example by 'scikit-image' gallery: + "Immunohistochemical staining colors separation" + https://scikit-image.org/docs/stable/auto_examples/color_exposure/plot_ihc_color_separation.html + """ + + def __init__(self, parent=None): + QMainWindow.__init__(self, parent) + self._main = QWidget() + self.setCentralWidget(self._main) + + # Main menu bar + self.menu = self.menuBar() + self.menu_file = self.menu.addMenu("File") + exit = QAction("Exit", self, triggered=qApp.quit) + self.menu_file.addAction(exit) + + self.menu_about = self.menu.addMenu("&About") + about = QAction("About Qt", self, shortcut=QKeySequence(QKeySequence.HelpContents), + triggered=qApp.aboutQt) + self.menu_about.addAction(about) + + # Create an artificial color close to the original one + self.ihc_rgb = data.immunohistochemistry() + self.ihc_hed = rgb2hed(self.ihc_rgb) + + main_layout = QVBoxLayout(self._main) + plot_layout = QHBoxLayout() + button_layout = QHBoxLayout() + label_layout = QHBoxLayout() + + self.canvas1 = FigureCanvas(Figure(figsize=(5, 5))) + self.canvas2 = FigureCanvas(Figure(figsize=(5, 5))) + + self._ax1 = self.canvas1.figure.subplots() + self._ax2 = self.canvas2.figure.subplots() + + self._ax1.imshow(self.ihc_rgb) + + plot_layout.addWidget(self.canvas1) + plot_layout.addWidget(self.canvas2) + + self.button1 = QPushButton("Hematoxylin") + self.button2 = QPushButton("Eosin") + self.button3 = QPushButton("DAB") + self.button4 = QPushButton("Fluorescence") + + self.button1.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding) + self.button2.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding) + self.button3.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding) + self.button4.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding) + + self.button1.clicked.connect(self.plot_hematoxylin) + self.button2.clicked.connect(self.plot_eosin) + self.button3.clicked.connect(self.plot_dab) + self.button4.clicked.connect(self.plot_final) + + self.label1 = QLabel("Original", alignment=Qt.AlignCenter) + self.label2 = QLabel("", alignment=Qt.AlignCenter) + + font = self.label1.font() + font.setPointSize(16) + self.label1.setFont(font) + self.label2.setFont(font) + + label_layout.addWidget(self.label1) + label_layout.addWidget(self.label2) + + button_layout.addWidget(self.button1) + button_layout.addWidget(self.button2) + button_layout.addWidget(self.button3) + button_layout.addWidget(self.button4) + + main_layout.addLayout(label_layout, 2) + main_layout.addLayout(plot_layout, 88) + main_layout.addLayout(button_layout, 10) + + # Default image + self.plot_hematoxylin() + + def set_buttons_state(self, states): + self.button1.setEnabled(states[0]) + self.button2.setEnabled(states[1]) + self.button3.setEnabled(states[2]) + self.button4.setEnabled(states[3]) + + @Slot() + def plot_hematoxylin(self): + cmap_hema = LinearSegmentedColormap.from_list("mycmap", ["white", "navy"]) + self._ax2.imshow(self.ihc_hed[:, :, 0], cmap=cmap_hema) + self.canvas2.draw() + self.label2.setText("Hematoxylin") + self.set_buttons_state((False, True, True, True)) + + @Slot() + def plot_eosin(self): + cmap_eosin = LinearSegmentedColormap.from_list("mycmap", ["darkviolet", "white"]) + self._ax2.imshow(self.ihc_hed[:, :, 1], cmap=cmap_eosin) + self.canvas2.draw() + self.label2.setText("Eosin") + self.set_buttons_state((True, False, True, True)) + + @Slot() + def plot_dab(self): + cmap_dab = LinearSegmentedColormap.from_list("mycmap", ["white", "saddlebrown"]) + self._ax2.imshow(self.ihc_hed[:, :, 2], cmap=cmap_dab) + self.canvas2.draw() + self.label2.setText("DAB") + self.set_buttons_state((True, True, False, True)) + + @Slot() + def plot_final(self): + h = rescale_intensity(self.ihc_hed[:, :, 0], out_range=(0, 1)) + d = rescale_intensity(self.ihc_hed[:, :, 2], out_range=(0, 1)) + zdh = np.dstack((np.zeros_like(h), d, h)) + self._ax2.imshow(zdh) + self.canvas2.draw() + self.label2.setText("Stain separated image") + self.set_buttons_state((True, True, True, False)) + + +if __name__ == "__main__": + + app = QApplication(sys.argv) + w = ApplicationWindow() + w.show() + app.exec_() -- cgit v1.2.3