diff --git a/xmidas/css/darkStyle.css b/xmidas/css/darkStyle.css index a5e9ddb..ea2c89f 100644 --- a/xmidas/css/darkStyle.css +++ b/xmidas/css/darkStyle.css @@ -1,68 +1,68 @@ -QWidget { -background-color: rgb(60, 60, 60); -color: rgb(255, 255, 255); -font: 10pt "Segoe UI"; -} - -QPushButton { -background-color: rgb(175, 236, 255); -color: rgb(255, 5,0); -} - -QSlider::groove:horizontal { -border: 1px solid #bbb; -background: white; -height: 10px; -border-radius: 4px; -} - -QSlider::sub-page:horizontal { -background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #66e, stop: 1 #bbf); -background: qlineargradient(x1: 0, y1: 0.2, x2: 1, y2: 1, - stop: 0 #bbf, stop: 1 #55f); -border: 1px solid #777; -height: 10px; -border-radius: 4px; -} - -QSlider::add-page:horizontal { -background: #fff; -border: 1px solid #777; -height: 10px; -border-radius: 4px; -} - -QSlider::handle:horizontal { -background: qlineargradient(x1:0, y1:0, x2:1, y2:1, - stop:0 #eee, stop:1 #ccc); -border: 1px solid #777; -width: 13px; -margin-top: -2px; -margin-bottom: -2px; -border-radius: 4px; -} - -QSlider::handle:horizontal:hover { -background: qlineargradient(x1:0, y1:0, x2:1, y2:1, - stop:0 #fff, stop:1 #ddd); -border: 1px solid #444; -border-radius: 2px; -} - -QSlider::sub-page:horizontal:disabled { -background: #bbb; -border-color: #999; -} - -QSlider::add-page:horizontal:disabled { -background: #eee; -border-color: #999; -} - -QSlider::handle:horizontal:disabled { -background: #eee; -border: 1px solid #aaa; -border-radius: 4px; -} - +QWidget { +background-color: rgb(60, 60, 60); +color: rgb(255, 255, 255); +font: 10pt "Segoe UI"; +} + +QPushButton { +background-color: rgb(175, 236, 255); +color: rgb(255, 5,0); +} + +QSlider::groove:horizontal { +border: 1px solid #bbb; +background: white; +height: 10px; +border-radius: 4px; +} + +QSlider::sub-page:horizontal { +background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #66e, stop: 1 #bbf); +background: qlineargradient(x1: 0, y1: 0.2, x2: 1, y2: 1, + stop: 0 #bbf, stop: 1 #55f); +border: 1px solid #777; +height: 10px; +border-radius: 4px; +} + +QSlider::add-page:horizontal { +background: #fff; +border: 1px solid #777; +height: 10px; +border-radius: 4px; +} + +QSlider::handle:horizontal { +background: qlineargradient(x1:0, y1:0, x2:1, y2:1, + stop:0 #eee, stop:1 #ccc); +border: 1px solid #777; +width: 13px; +margin-top: -2px; +margin-bottom: -2px; +border-radius: 4px; +} + +QSlider::handle:horizontal:hover { +background: qlineargradient(x1:0, y1:0, x2:1, y2:1, + stop:0 #fff, stop:1 #ddd); +border: 1px solid #444; +border-radius: 2px; +} + +QSlider::sub-page:horizontal:disabled { +background: #bbb; +border-color: #999; +} + +QSlider::add-page:horizontal:disabled { +background: #eee; +border-color: #999; +} + +QSlider::handle:horizontal:disabled { +background: #eee; +border: 1px solid #aaa; +border-radius: 4px; +} + diff --git a/xmidas/css/defaultStyle.css b/xmidas/css/defaultStyle.css index 981a787..11c2467 100644 --- a/xmidas/css/defaultStyle.css +++ b/xmidas/css/defaultStyle.css @@ -1,72 +1,72 @@ - -QWidget { -font: 10pt "Segoe UI"; -} - -QPushButton { -background-color: rgb(175, 236, 255); -color: rgb(255, 5,0); -font: 10pt "Segoe UI"; -} - -QLabel { -font: 10pt "Segoe UI"; -} - -QSlider::groove:horizontal { -border: 1px solid #bbb; -background: white; -height: 10px; -border-radius: 4px; -} - -QSlider::sub-page:horizontal { -background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #66e, stop: 1 #bbf); -background: qlineargradient(x1: 0, y1: 0.2, x2: 1, y2: 1, - stop: 0 #bbf, stop: 1 #55f); -border: 1px solid #777; -height: 10px; -border-radius: 4px; -} - -QSlider::add-page:horizontal { -background: #fff; -border: 1px solid #777; -height: 10px; -border-radius: 4px; -} - -QSlider::handle:horizontal { -background: qlineargradient(x1:0, y1:0, x2:1, y2:1, - stop:0 #eee, stop:1 #ccc); -border: 1px solid #777; -width: 13px; -margin-top: -2px; -margin-bottom: -2px; -border-radius: 4px; -} - -QSlider::handle:horizontal:hover { -background: qlineargradient(x1:0, y1:0, x2:1, y2:1, - stop:0 #fff, stop:1 #ddd); -border: 1px solid #444; -border-radius: 2px; -} - -QSlider::sub-page:horizontal:disabled { -background: #bbb; -border-color: #999; -} - -QSlider::add-page:horizontal:disabled { -background: #eee; -border-color: #999; -} - -QSlider::handle:horizontal:disabled { -background: #eee; -border: 1px solid #aaa; -border-radius: 4px; -} - + +QWidget { +font: 10pt "Segoe UI"; +} + +QPushButton { +background-color: rgb(175, 236, 255); +color: rgb(255, 5,0); +font: 10pt "Segoe UI"; +} + +QLabel { +font: 10pt "Segoe UI"; +} + +QSlider::groove:horizontal { +border: 1px solid #bbb; +background: white; +height: 10px; +border-radius: 4px; +} + +QSlider::sub-page:horizontal { +background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #66e, stop: 1 #bbf); +background: qlineargradient(x1: 0, y1: 0.2, x2: 1, y2: 1, + stop: 0 #bbf, stop: 1 #55f); +border: 1px solid #777; +height: 10px; +border-radius: 4px; +} + +QSlider::add-page:horizontal { +background: #fff; +border: 1px solid #777; +height: 10px; +border-radius: 4px; +} + +QSlider::handle:horizontal { +background: qlineargradient(x1:0, y1:0, x2:1, y2:1, + stop:0 #eee, stop:1 #ccc); +border: 1px solid #777; +width: 13px; +margin-top: -2px; +margin-bottom: -2px; +border-radius: 4px; +} + +QSlider::handle:horizontal:hover { +background: qlineargradient(x1:0, y1:0, x2:1, y2:1, + stop:0 #fff, stop:1 #ddd); +border: 1px solid #444; +border-radius: 2px; +} + +QSlider::sub-page:horizontal:disabled { +background: #bbb; +border-color: #999; +} + +QSlider::add-page:horizontal:disabled { +background: #eee; +border-color: #999; +} + +QSlider::handle:horizontal:disabled { +background: #eee; +border: 1px solid #aaa; +border-radius: 4px; +} + diff --git a/xmidas/css/modern.css b/xmidas/css/modern.css index 1bc5d20..a949b73 100644 --- a/xmidas/css/modern.css +++ b/xmidas/css/modern.css @@ -1,289 +1,289 @@ -/* ///////////////////////////////////////////////////////////////////////////////////////////////// - -SET APP STYLESHEET - FULL STYLES HERE -DARK THEME - DRACULA COLOR BASED - -///////////////////////////////////////////////////////////////////////////////////////////////// */ - -QWidget{ - background-color: rgb(23,23,37); - color: rgb(255, 255, 200); - font: 10pt "Segoe UI"; -} - - - -/* ///////////////////////////////////////////////////////////////////////////////////////////////// -Tooltip */ -QToolTip { - color: #ffffff; - background-color: rgba(33, 37, 43, 180); - border: 1px solid rgb(44, 49, 58); - background-image: none; - background-position: left center; - background-repeat: no-repeat; - border: none; - border-left: 2px solid rgb(255, 121, 198); - text-align: left; - padding-left: 8px; - margin: 0px; -} - - -QPushButton { - background-color: rgb(210, 210, 98); - color: rgb(23,23,37); - border: none; - border-radius: 5px; - padding: 4px; -} -QPushButton:hover { - background-color: rgb(211, 100, 211); -} -QPushButton:pressed { - background-color: rgb(189, 147, 249); - color: rgb(255, 255, 255); -} - - -QTabWidget { - color: rgb(44, 0, 0); -} -QTabWidget::item{ - color: rgb(24, 0, 0); -} -QTabBar::tab { - color:rgb(24, 0, 0); -} - -QLineEdit { - background-color: rgb(33, 37, 43); - border-radius: 5px; - border: 2px solid rgb(33, 37, 43); - padding-left: 10px; - color: rgb(255,255,255); - selection-color: rgb(255, 255, 255); - selection-background-color: rgb(255, 121, 198); -} -QLineEdit:hover { - border: 2px solid rgb(64, 71, 88); -} -QLineEdit:focus { - border: 2px solid rgb(91, 101, 124); -} - -/* ///////////////////////////////////////////////////////////////////////////////////////////////// -PlainTextEdit */ -QPlainTextEdit { - background-color: rgb(27, 29, 35); - border-radius: 5px; - padding: 10px; - selection-color: rgb(255, 255, 255); - selection-background-color: rgb(255, 121, 198); -} -QPlainTextEdit QScrollBar:vertical { - width: 8px; - } -QPlainTextEdit QScrollBar:horizontal { - height: 8px; - } -QPlainTextEdit:hover { - border: 2px solid rgb(64, 71, 88); -} -QPlainTextEdit:focus { - border: 2px solid rgb(91, 101, 124); -} - -/* ///////////////////////////////////////////////////////////////////////////////////////////////// -ScrollBars */ -QScrollBar:horizontal { - border: none; - background: rgb(52, 59, 72); - height: 8px; - margin: 0px 21px 0 21px; - border-radius: 0px; -} -QScrollBar::handle:horizontal { - background: rgb(189, 147, 249); - min-width: 25px; - border-radius: 4px -} -QScrollBar::add-line:horizontal { - border: none; - background: rgb(55, 63, 77); - width: 20px; - border-top-right-radius: 4px; - border-bottom-right-radius: 4px; - subcontrol-position: right; - subcontrol-origin: margin; -} -QScrollBar::sub-line:horizontal { - border: none; - background: rgb(55, 63, 77); - width: 20px; - border-top-left-radius: 4px; - border-bottom-left-radius: 4px; - subcontrol-position: left; - subcontrol-origin: margin; -} -QScrollBar::up-arrow:horizontal, QScrollBar::down-arrow:horizontal -{ - background: none; -} -QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal -{ - background: none; -} - QScrollBar:vertical { - border: none; - background: rgb(52, 59, 72); - width: 8px; - margin: 21px 0 21px 0; - border-radius: 0px; - } - QScrollBar::handle:vertical { - background: rgb(189, 147, 249); - min-height: 25px; - border-radius: 4px - } - QScrollBar::add-line:vertical { - border: none; - background: rgb(55, 63, 77); - height: 20px; - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; - subcontrol-position: bottom; - subcontrol-origin: margin; - } - QScrollBar::sub-line:vertical { - border: none; - background: rgb(55, 63, 77); - height: 20px; - border-top-left-radius: 4px; - border-top-right-radius: 4px; - subcontrol-position: top; - subcontrol-origin: margin; - } - QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical { - background: none; - } - - QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { - background: none; - } - - -QRadioButton::indicator { - border: 3px solid rgb(52, 59, 72); - width: 15px; - height: 15px; - border-radius: 10px; - background: rgb(44, 4, 0); -} -QRadioButton::indicator:hover { - border: 3px solid rgb(58, 66, 81); -} -QRadioButton::indicator:checked { - background: 3px solid rgb(0, 255, 0); - border: 3px solid rgb(255, 252, 255); -} - -QCheckBox::indicator { - border: 3px solid rgb(52, 59, 72); - width: 15px; - height: 15px; - border-radius: 10px; - background: rgb(44, 4, 0); -} -QCheckBox::indicator:hover { - border: 3px solid rgb(58, 66, 81); -} -QCheckBox::indicator:checked { - background: 3px solid rgb(0, 255, 0); - border: 3px solid rgb(255, 252, 255); -} - -/* ///////////////////////////////////////////////////////////////////////////////////////////////// -ComboBox */ -QComboBox{ - background-color: rgb(138, 138, 138); - color: rgb(79, 27, 79); - border-radius: 5px; - border: 2px solid rgb(33, 37, 43); - padding: 5px; - padding-left: 10px; -} -QComboBox:hover{ - border: 2px solid rgb(64, 71, 88); -} -QComboBox::drop-down { - subcontrol-origin: padding; - subcontrol-position: top right; - width: 25px; - border-left-width: 3px; - border-left-color: rgba(39, 44, 54, 150); - border-left-style: solid; - border-top-right-radius: 3px; - border-bottom-right-radius: 3px; - background-image: url(:/icons/images/icons/cil-arrow-bottom.png); - background-position: center; - background-repeat: no-reperat; - } -QComboBox QAbstractItemView { - color: rgb(255, 121, 198); - background-color: rgb(33, 37, 43); - padding: 10px; - selection-background-color: rgb(39, 44, 54); -} - -/* ///////////////////////////////////////////////////////////////////////////////////////////////// -Sliders */ -QSlider::groove:horizontal { - border-radius: 5px; - height: 10px; - margin: 0px; - background-color: rgb(52, 59, 72); -} -QSlider::groove:horizontal:hover { - background-color: rgb(55, 62, 76); -} -QSlider::handle:horizontal { - background-color: rgb(189, 147, 249); - border: none; - height: 10px; - width: 10px; - margin: 0px; - border-radius: 5px; -} -QSlider::handle:horizontal:hover { - background-color: rgb(195, 155, 255); -} -QSlider::handle:horizontal:pressed { - background-color: rgb(255, 121, 198); -} - -QSlider::groove:vertical { - border-radius: 5px; - width: 10px; - margin: 0px; - background-color: rgb(52, 59, 72); -} -QSlider::groove:vertical:hover { - background-color: rgb(55, 62, 76); -} -QSlider::handle:vertical { - background-color: rgb(189, 147, 249); - border: none; - height: 10px; - width: 10px; - margin: 0px; - border-radius: 5px; -} -QSlider::handle:vertical:hover { - background-color: rgb(195, 155, 255); -} -QSlider::handle:vertical:pressed { - background-color: rgb(255, 121, 198); -} - - +/* ///////////////////////////////////////////////////////////////////////////////////////////////// + +SET APP STYLESHEET - FULL STYLES HERE +DARK THEME - DRACULA COLOR BASED + +///////////////////////////////////////////////////////////////////////////////////////////////// */ + +QWidget{ + background-color: rgb(23,23,37); + color: rgb(255, 255, 200); + font: 10pt "Segoe UI"; +} + + + +/* ///////////////////////////////////////////////////////////////////////////////////////////////// +Tooltip */ +QToolTip { + color: #ffffff; + background-color: rgba(33, 37, 43, 180); + border: 1px solid rgb(44, 49, 58); + background-image: none; + background-position: left center; + background-repeat: no-repeat; + border: none; + border-left: 2px solid rgb(255, 121, 198); + text-align: left; + padding-left: 8px; + margin: 0px; +} + + +QPushButton { + background-color: rgb(210, 210, 98); + color: rgb(23,23,37); + border: none; + border-radius: 5px; + padding: 4px; +} +QPushButton:hover { + background-color: rgb(211, 100, 211); +} +QPushButton:pressed { + background-color: rgb(189, 147, 249); + color: rgb(255, 255, 255); +} + + +QTabWidget { + color: rgb(44, 0, 0); +} +QTabWidget::item{ + color: rgb(24, 0, 0); +} +QTabBar::tab { + color:rgb(24, 0, 0); +} + +QLineEdit { + background-color: rgb(33, 37, 43); + border-radius: 5px; + border: 2px solid rgb(33, 37, 43); + padding-left: 10px; + color: rgb(255,255,255); + selection-color: rgb(255, 255, 255); + selection-background-color: rgb(255, 121, 198); +} +QLineEdit:hover { + border: 2px solid rgb(64, 71, 88); +} +QLineEdit:focus { + border: 2px solid rgb(91, 101, 124); +} + +/* ///////////////////////////////////////////////////////////////////////////////////////////////// +PlainTextEdit */ +QPlainTextEdit { + background-color: rgb(27, 29, 35); + border-radius: 5px; + padding: 10px; + selection-color: rgb(255, 255, 255); + selection-background-color: rgb(255, 121, 198); +} +QPlainTextEdit QScrollBar:vertical { + width: 8px; + } +QPlainTextEdit QScrollBar:horizontal { + height: 8px; + } +QPlainTextEdit:hover { + border: 2px solid rgb(64, 71, 88); +} +QPlainTextEdit:focus { + border: 2px solid rgb(91, 101, 124); +} + +/* ///////////////////////////////////////////////////////////////////////////////////////////////// +ScrollBars */ +QScrollBar:horizontal { + border: none; + background: rgb(52, 59, 72); + height: 8px; + margin: 0px 21px 0 21px; + border-radius: 0px; +} +QScrollBar::handle:horizontal { + background: rgb(189, 147, 249); + min-width: 25px; + border-radius: 4px +} +QScrollBar::add-line:horizontal { + border: none; + background: rgb(55, 63, 77); + width: 20px; + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; + subcontrol-position: right; + subcontrol-origin: margin; +} +QScrollBar::sub-line:horizontal { + border: none; + background: rgb(55, 63, 77); + width: 20px; + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; + subcontrol-position: left; + subcontrol-origin: margin; +} +QScrollBar::up-arrow:horizontal, QScrollBar::down-arrow:horizontal +{ + background: none; +} +QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal +{ + background: none; +} + QScrollBar:vertical { + border: none; + background: rgb(52, 59, 72); + width: 8px; + margin: 21px 0 21px 0; + border-radius: 0px; + } + QScrollBar::handle:vertical { + background: rgb(189, 147, 249); + min-height: 25px; + border-radius: 4px + } + QScrollBar::add-line:vertical { + border: none; + background: rgb(55, 63, 77); + height: 20px; + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + subcontrol-position: bottom; + subcontrol-origin: margin; + } + QScrollBar::sub-line:vertical { + border: none; + background: rgb(55, 63, 77); + height: 20px; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + subcontrol-position: top; + subcontrol-origin: margin; + } + QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical { + background: none; + } + + QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { + background: none; + } + + +QRadioButton::indicator { + border: 3px solid rgb(52, 59, 72); + width: 15px; + height: 15px; + border-radius: 10px; + background: rgb(44, 4, 0); +} +QRadioButton::indicator:hover { + border: 3px solid rgb(58, 66, 81); +} +QRadioButton::indicator:checked { + background: 3px solid rgb(0, 255, 0); + border: 3px solid rgb(255, 252, 255); +} + +QCheckBox::indicator { + border: 3px solid rgb(52, 59, 72); + width: 15px; + height: 15px; + border-radius: 10px; + background: rgb(44, 4, 0); +} +QCheckBox::indicator:hover { + border: 3px solid rgb(58, 66, 81); +} +QCheckBox::indicator:checked { + background: 3px solid rgb(0, 255, 0); + border: 3px solid rgb(255, 252, 255); +} + +/* ///////////////////////////////////////////////////////////////////////////////////////////////// +ComboBox */ +QComboBox{ + background-color: rgb(138, 138, 138); + color: rgb(79, 27, 79); + border-radius: 5px; + border: 2px solid rgb(33, 37, 43); + padding: 5px; + padding-left: 10px; +} +QComboBox:hover{ + border: 2px solid rgb(64, 71, 88); +} +QComboBox::drop-down { + subcontrol-origin: padding; + subcontrol-position: top right; + width: 25px; + border-left-width: 3px; + border-left-color: rgba(39, 44, 54, 150); + border-left-style: solid; + border-top-right-radius: 3px; + border-bottom-right-radius: 3px; + background-image: url(:/icons/images/icons/cil-arrow-bottom.png); + background-position: center; + background-repeat: no-reperat; + } +QComboBox QAbstractItemView { + color: rgb(255, 121, 198); + background-color: rgb(33, 37, 43); + padding: 10px; + selection-background-color: rgb(39, 44, 54); +} + +/* ///////////////////////////////////////////////////////////////////////////////////////////////// +Sliders */ +QSlider::groove:horizontal { + border-radius: 5px; + height: 10px; + margin: 0px; + background-color: rgb(52, 59, 72); +} +QSlider::groove:horizontal:hover { + background-color: rgb(55, 62, 76); +} +QSlider::handle:horizontal { + background-color: rgb(189, 147, 249); + border: none; + height: 10px; + width: 10px; + margin: 0px; + border-radius: 5px; +} +QSlider::handle:horizontal:hover { + background-color: rgb(195, 155, 255); +} +QSlider::handle:horizontal:pressed { + background-color: rgb(255, 121, 198); +} + +QSlider::groove:vertical { + border-radius: 5px; + width: 10px; + margin: 0px; + background-color: rgb(52, 59, 72); +} +QSlider::groove:vertical:hover { + background-color: rgb(55, 62, 76); +} +QSlider::handle:vertical { + background-color: rgb(189, 147, 249); + border: none; + height: 10px; + width: 10px; + margin: 0px; + border-radius: 5px; +} +QSlider::handle:vertical:hover { + background-color: rgb(195, 155, 255); +} +QSlider::handle:vertical:pressed { + background-color: rgb(255, 121, 198); +} + + diff --git a/xmidas/main.py b/xmidas/main.py index 3185d3c..c2c2236 100644 --- a/xmidas/main.py +++ b/xmidas/main.py @@ -1,4299 +1,4019 @@ -# -*- coding: utf-8 -*- - -# Author: Ajith Pattammattel -# First Version on:06-23-2020 - -import argparse -import logging -import sys -import webbrowser -import traceback -import os -import json -import h5py -import scipy.stats as stats -import numpy as np -import pandas as pd -import tifffile as tf -import pyqtgraph as pg -import pyqtgraph.exporters -import scipy.optimize as opt -import sklearn.decomposition as sd -import sklearn.cluster as sc - -from pyqtgraph import plot -from itertools import combinations -from scipy.stats import linregress -from scipy.signal import savgol_filter -from skimage.transform import resize -from skimage import filters -from sklearn import linear_model -from larch.xafs import preedge -from pystackreg import StackReg -from packaging import version - -from PyQt5 import QtWidgets, QtCore, QtGui, uic, QtTest -from PyQt5.QtGui import QMovie -from PyQt5.QtWidgets import QMessageBox, QFileDialog, QApplication -from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot, QRunnable, QThreadPool, PYQT_VERSION_STR - -from . import __version__ - -# from MultiChannel import * - -logger = logging.getLogger() -try: - import cv2 # noqa: F401 -except Exception: - logger.warning("openCV module not found") - pass -if hasattr(QtCore.Qt, "AA_EnableHighDpiScaling"): - QtWidgets.QApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling, True) - -if hasattr(QtCore.Qt, "AA_UseHighDpiPixmaps"): - QtWidgets.QApplication.setAttribute(QtCore.Qt.AA_UseHighDpiPixmaps, True) - -ui_path = os.path.dirname(os.path.abspath(__file__)) - -# global settings for pyqtgraph plot and image colormaps -pg.setConfigOption("imageAxisOrder", "row-major") -cmap_names = ["CET-L13", "CET-L14", "CET-L15"] -cmap_combo = combinations(cmap_names, 2) -cmap_label1 = ["red", "green", "blue"] -cmap_label2 = ["yellow", "magenta", "cyan"] -cmap_dict = {} -for i, name in zip(cmap_names, cmap_label1): - cmap_dict[name] = pg.colormap.get(i).getLookupTable(alpha=True) - -for i, name in zip(cmap_combo, cmap_label2): - cmap_dict[name] = pg.colormap.get(i[0]).getLookupTable(alpha=True) + pg.colormap.get(i[1]).getLookupTable( - alpha=True - ) - cmap_dict[name][:, 3] = 255 - - grey = ( - pg.colormap.get("CET-L13").getLookupTable(alpha=True) - + pg.colormap.get("CET-L14").getLookupTable(alpha=True) - + pg.colormap.get("CET-L15").getLookupTable(alpha=True) - ) - - grey[:, 3] = 255 - cmap_dict["grey"] = grey - - -class jsonEncoder(json.JSONEncoder): - def default(self, obj): - if isinstance(obj, np.integer): - return int(obj) - elif isinstance(obj, np.floating): - return float(obj) - elif isinstance(obj, np.ndarray): - return obj.tolist() - else: - return super(jsonEncoder, self).default(obj) - - -class midasWindow(QtWidgets.QMainWindow): - def __init__(self, im_stack=None, energy=[], refs=[]): - super(midasWindow, self).__init__() - uic.loadUi(os.path.join(ui_path, "uis/midasMainwindow.ui"), self) - self.im_stack = im_stack - self.energy = energy - self.refs = refs - self.loaded_tranform_file = [] - self.image_roi2_flag = False - self.refStackAvailable = False - self.isAReload = False - self.plotWidth = 2 - self.stackStatusDict = {} - - self.plt_colors = [ - "g", - "r", - "c", - "m", - "y", - "w", - "b", - pg.mkPen(70, 5, 80), - pg.mkPen(255, 85, 130), - pg.mkPen(0, 85, 130), - pg.mkPen(255, 170, 60), - ] * 3 - # window style - self.actionDarkMode.triggered.connect(self.darkMode) - self.actionDefault.triggered.connect(self.defaultMode) - self.actionModern.triggered.connect(self.modernMode) - - # self.setToolTipsVisible(True) - for menuItem in self.findChildren(QtWidgets.QMenu): - menuItem.setToolTipsVisible(True) - - # plotview options - self.actionWhite.triggered.connect(lambda: self.spectrum_view.setBackground("w")) - self.actionRed.triggered.connect(lambda: self.spectrum_view.setBackground("r")) - self.actionYellow.triggered.connect(lambda: self.spectrum_view.setBackground("y")) - self.actionBlue.triggered.connect(lambda: self.spectrum_view.setBackground("b")) - self.actionBlack.triggered.connect(lambda: self.spectrum_view.setBackground((0, 0, 0))) - - self.actn1.triggered.connect(lambda: self.setPlotLineWidth(int(self.actn1.text()))) - self.actn2.triggered.connect(lambda: self.setPlotLineWidth(int(self.actn2.text()))) - self.actn3.triggered.connect(lambda: self.setPlotLineWidth(int(self.actn3.text()))) - self.actn4.triggered.connect(lambda: self.setPlotLineWidth(int(self.actn4.text()))) - self.actn5.triggered.connect(lambda: self.setPlotLineWidth(int(self.actn5.text()))) - self.actn6.triggered.connect(lambda: self.setPlotLineWidth(int(self.actn6.text()))) - self.actn8.triggered.connect(lambda: self.setPlotLineWidth(int(self.actn8.text()))) - self.actn10.triggered.connect(lambda: self.setPlotLineWidth(int(self.actn10.text()))) - - self.actionOpen_Image_Data.triggered.connect(self.browse_file) - self.actionOpen_Multiple_Files.triggered.connect(self.createVirtualStack) - self.actionSave_as.triggered.connect(lambda: self.save_stack()) - self.actionExit.triggered.connect(lambda: QApplication.closeAllWindows()) - self.actionOpen_in_GitHub.triggered.connect(self.open_github_link) - self.actionLoad_Energy.triggered.connect(self.select_elist) - self.menuFile.setToolTipsVisible(True) - - # Accessories - self.actionOpen_Mask_Gen.triggered.connect(self.openMaskMaker) - self.actionMultiColor.triggered.connect(self.openMultiColorWindow) - - # calculations - self.pb_transpose_stack.clicked.connect(lambda: self.threadMaker(self.transposeStack)) - self.pb_swapXY_stack.clicked.connect(lambda: self.threadMaker(self.swapStackXY)) - self.pb_reset_img.clicked.connect(self.reloadImageStack) - self.pb_crop.clicked.connect(self.crop_to_dim) - self.pb_crop.clicked.connect(self.view_stack) - self.sb_scaling_factor.valueChanged.connect(self.view_stack) - self.pb_ref_xanes.clicked.connect(self.select_ref_file) - self.pb_elist_xanes.clicked.connect(self.select_elist) - - # batchjobs - self.actionPlotAllCorrelations.triggered.connect(self.plotCorrelationsAllCombinations) - - [ - uis.valueChanged.connect(self.replot_image) - for uis in [self.hs_smooth_size, self.hs_nsigma, self.hs_bg_threshold] - ] - - [ - uis.stateChanged.connect(self.replot_image) - for uis in [self.cb_remove_bg, self.cb_remove_outliers, self.cb_smooth, self.cb_norm, self.cb_log] - ] - - [ - uis.stateChanged.connect(self.view_stack) - for uis in [self.cb_remove_edges, self.cb_upscale, self.cb_rebin] - ] - - # ToolBar - self.actionStack_Info.triggered.connect(self.displayStackInfo) - self.actionSave_Image.triggered.connect(self.save_disp_img) - self.actionExport_Stack.triggered.connect(lambda: self.save_stack()) - - # ROI background - self.actionSubtract_ROI_BG.triggered.connect(lambda: self.threadMaker(self.removeROIBGStack)) - - # alignment - self.pb_load_align_ref.clicked.connect(self.loadAlignRefImage) - self.pb_loadAlignTranform.clicked.connect(self.importAlignTransformation) - self.pb_saveAlignTranform.clicked.connect(self.exportAlignTransformation) - self.pb_alignStack.clicked.connect(lambda: self.threadMaker(self.stackRegistration)) - # self.pb_alignStack.clicked.connect(self.stackRegistration) - - # save_options - self.actionSave_Sum_Image.triggered.connect(lambda: self.save_stack(method="sum")) - self.actionSave_Mean_Image.triggered.connect(lambda: self.save_stack(method="mean")) - self.pb_save_disp_spec.clicked.connect(self.save_disp_spec) - self.actionSave_Energy_List.triggered.connect(self.saveEnergyList) - self.pb_show_roi.clicked.connect(self.getROIMask) - self.pb_addToCollector.clicked.connect(self.addSpectrumToCollector) - self.pb_collect_clear.clicked.connect(lambda: self.spectrum_view_collect.clear()) - self.pb_saveCollectorPlot.clicked.connect(self.saveCollectorPlot) - - # XANES Normalization - self.pb_apply_xanes_norm.clicked.connect(self.nomalizeLiveSpec) - self.pb_auto_Eo.clicked.connect(self.findEo) - self.pb_xanes_norm_vals.clicked.connect(self.initNormVals) - self.pb_apply_norm_to_stack.clicked.connect(lambda: self.threadMaker(self.normalizeStack)) - self.actionExport_Norm_Params.triggered.connect(self.exportNormParams) - self.actionImport_Norm_Params.triggered.connect(self.importNormParams) - - # Analysis - self.pb_pca_scree.clicked.connect(self.pca_scree_) - self.pb_calc_components.clicked.connect(self.calc_comp_) - self.pb_kmeans_elbow.clicked.connect(self.kmeans_elbow) - self.pb_calc_cluster.clicked.connect(self.clustering_) - self.pb_xanes_fit.clicked.connect(self.fast_xanes_fitting) - self.pb_plot_refs.clicked.connect(self.plt_xanes_refs) - - self.show() - - self.threadpool = QThreadPool() - print(f"Multithreading with maximum {self.threadpool.maxThreadCount()} threads") - - # View Options - def darkMode(self): - self.centralwidget.setStyleSheet(open(os.path.join(ui_path, "css/darkStyle.css")).read()) - - def defaultMode(self): - self.centralwidget.setStyleSheet(open(os.path.join(ui_path, "css/defaultStyle.css")).read()) - - def modernMode(self): - self.centralwidget.setStyleSheet(open(os.path.join(ui_path, "css/modern.css")).read()) - - def setPlotLineWidth(self, width_input): - self.plotWidth = width_input - try: - self.update_spectrum() - except Exception: - pass - - def openMultiColorWindow(self): - self.multicolorwindow = MultiChannelWindow() - self.multicolorwindow.show() - - def openMaskMaker(self): - self.mask_window = MaskSpecViewer(xanes_stack=self.displayedStack, energy=self.energy) - self.mask_window.show() - - def open_github_link(self): - webbrowser.open("https://github.com/pattammattel/NSLS-II-MIDAS/wiki") - - def threadMaker(self, funct): - # Pass the function to execute - worker = Worker(funct) # Any other args, kwargs are passed to the run function - self.loadSplashScreen() - worker.signals.start.connect(self.splash.startAnimation) - worker.signals.result.connect(self.print_output) - - list( - map( - worker.signals.finished.connect, - [ - self.thread_complete, - self.splash.stopAnimation, - self.update_stack_info, - self.update_spectrum, - self.update_image_roi, - ], - ) - ) - - # Execute - self.threadpool.start(worker) - - # File Loading - - def createVirtualStack(self): - """User can load multiple/series of tiff images with same shape. - The 'self.load_stack()' recognizes 'self.filename as list and create the stack. - """ - self.energy = [] - filter = "TIFF (*.tiff);;TIF (*.tif);;all_files (*)" - file_name = QFileDialog() - file_name.setFileMode(QFileDialog.ExistingFiles) - names = file_name.getOpenFileNames(self, "Open files", " ", filter) - if names[0]: - - self.file_name = names[0] - self.load_stack() - - else: - self.statusbar_main.showMessage("No file has selected") - pass - - def load_stack(self): - - """load the image data from the selected file. - If the the choice is for multiple files stack will be created in a loop. - If single h5 file is selected the unpacking will be done with 'get_xrf_data' function in StackCalcs. - From the h5 the program can recognize the beamline. The exported stack will be normalized to I0. - - If the single tiff file is choosen tf.imread() is used. - - The output 'self.im_stack' is the unmodified data file - """ - - self.log_warning = False # for the Qmessage box in cb_log - self.image_roi2_flag = False - self.cb_log.setChecked(False) - self.cb_remove_edges.setChecked(False) - self.cb_norm.setChecked(False) - self.cb_smooth.setChecked(False) - self.cb_remove_outliers.setChecked(False) - self.cb_remove_bg.setChecked(False) - self.cb_rebin.setChecked(False) - self.cb_upscale.setChecked(False) - self.sb_xrange1.setValue(0) - self.sb_yrange1.setValue(0) - self.sb_zrange1.setValue(0) - - self.menuMask.setEnabled(True) - self.actionLoad_Energy.setEnabled(True) - self.actionSave_Energy_List.setEnabled(True) - self.actionSave_as.setEnabled(True) - - self.sb_zrange2.setMaximum(99999) - self.sb_xrange2.setMaximum(99999) - self.sb_yrange2.setMaximum(99999) - - self.statusbar_main.showMessage("Loading.. please wait...") - - if isinstance(self.file_name, list): - - all_images = [] - - for im_file in self.file_name: - img = tf.imread(im_file) - all_images.append(img) # row major image - self.im_stack = np.dstack(all_images).transpose((2, 0, 1)) - self.avgIo = 1 # I0 is only applicable to XRF h5 files - self.sb_zrange2.setValue(self.im_stack.shape[0]) - - else: - - if self.file_name.endswith(".h5"): - self.im_stack, mono_e, bl_name, self.avgIo = get_xrf_data(self.file_name) - self.statusbar_main.showMessage(f"Data from {bl_name}") - self.sb_zrange2.setValue(mono_e / 10) - self.energy = [] - - elif self.file_name.endswith(".tiff") or self.file_name.endswith(".tif"): - self.im_stack_ = tf.imread(self.file_name) - if self.im_stack_.ndim == 2: - self.im_stack = self.im_stack_.reshape(1, self.im_stack_.shape[0], self.im_stack_.shape[1]) - - else: - self.im_stack = self.im_stack_ - self.sb_zrange2.setValue(self.im_stack.shape[0]) - self.autoEnergyLoader() - self.energyUnitCheck() - self.avgIo = 1 - - else: - logger.error("Unknown data format") - - """ Fill the stack dimensions to the GUI and set the image dimensions as max values. - This prevent user from choosing higher image dimensions during a resizing event""" - - logger.info(f" loaded stack with {np.shape(self.im_stack)} from the file") - - try: - logger.info(f" Transposed to shape: {np.shape(self.im_stack)}") - self.init_dimZ, self.init_dimY, self.init_dimX = self.im_stack.shape - # Remove any previously set max value during a reload - - self.sb_xrange2.setValue(self.init_dimX) - self.sb_yrange2.setValue(self.init_dimY) - - except UnboundLocalError: - logger.error("No file selected") - pass - - self.view_stack() - logger.info("Stack displayed correctly") - self.update_stack_info() - - logger.info(f"completed image shape {np.shape(self.im_stack)}") - - try: - self.statusbar_main.showMessage(f"Loaded: {self.file_name}") - - except AttributeError: - self.statusbar_main.showMessage("New Stack is made from selected tiffs") - pass - - def browse_file(self): - """To open a file widow and choose the data file. - The filename will be used to load data using 'rest and load stack' function""" - - filename = QFileDialog().getOpenFileName( - self, "Select image data", "", "image file(*.hdf *.h5 *tiff *tif )" - ) - self.file_name = str(filename[0]) - - # if user decides to cancel the file window gui returns to original state - if self.file_name: - self.disconnectImageActions() - self.isAReload = False - self.load_stack() - - else: - self.statusbar_main.showMessage("No file has selected") - pass - - def autoEnergyLoader(self): - - dir_, filename_ = os.path.split(self.file_name) - self.efilePath_name = os.path.join(dir_, os.path.splitext(filename_)[0] + ".txt") - self.efilePath_log = os.path.join(dir_, "maps_log_tiff.txt") - - if os.path.isfile(self.efilePath_name): - self.efilePath = self.efilePath_name - self.efileLoader() - self.statusbar_main.showMessage(f"Energy File detected {self.efilePath}") - - elif os.path.isfile(self.efilePath_log): - self.efilePath = self.efilePath_log - self.efileLoader() - self.statusbar_main.showMessage(f"Energy File detected {self.efilePath}") - - else: - self.efilePath = False - self.efileLoader() - - def update_stack_info(self): - z, y, x = np.shape(self.displayedStack) - self.sb_zrange2.setMaximum(z + self.sb_zrange1.value()) - self.sb_xrange2.setValue(x) - self.sb_xrange2.setMaximum(x) - self.sb_yrange2.setValue(y) - self.sb_yrange2.setMaximum(y) - logger.info("Stack info has been updated") - - # Image Transformations - - def crop_to_dim(self): - self.x1, self.x2 = self.sb_xrange1.value(), self.sb_xrange2.value() - self.y1, self.y2 = self.sb_yrange1.value(), self.sb_yrange2.value() - self.z1, self.z2 = self.sb_zrange1.value(), self.sb_zrange2.value() - - try: - self.displayedStack = remove_nan_inf( - self.displayedStack[self.z1 : self.z2, self.y1 : self.y2, self.x1 : self.x2] - ) - except Exception: - self.displayedStack = remove_nan_inf( - self.im_stack[self.z1 : self.z2, self.y1 : self.y2, self.x1 : self.x2] - ) - - def transpose_stack(self): - self.displayedStack = self.displayedStack.T - self.update_spectrum() - self.update_spec_image_roi() - - # Alignement - - def loadAlignRefImage(self): - filename = QFileDialog().getOpenFileName(self, "Image Data", "", "*.tiff *.tif") - file_name = str(filename[0]) - self.alignRefImage = tf.imread(file_name) - assert self.alignRefImage.shape == self.displayedStack.shape, "Image dimensions do not match" - self.refStackAvailable = True - self.rb_alignRefVoid.setChecked(False) - self.change_color_on_load(self.pb_load_align_ref) - - def stackRegistration(self): - - self.transformations = { - "TRANSLATION": StackReg.TRANSLATION, - "RIGID_BODY": StackReg.RIGID_BODY, - "SCALED_ROTATION": StackReg.SCALED_ROTATION, - "AFFINE": StackReg.AFFINE, - "BILINEAR": StackReg.BILINEAR, - } - - self.transformType = self.transformations[self.cb_alignTransform.currentText()] - self.alignReferenceImage = self.cb_alignRef.currentText() - self.alignRefStackVoid = self.rb_alignRefVoid.isChecked() - self.alignMaxIter = self.sb_maxIterVal.value() - - if self.cb_use_tmatFile.isChecked(): - - if len(self.loaded_tranform_file) > 0: - - self.displayedStack = align_with_tmat( - self.displayedStack, tmat_file=self.loaded_tranform_file, transformation=self.transformType - ) - logger.info("Aligned to the tranform File") - - else: - logger.error("No Tranformation File Loaded") - - elif self.cb_iterAlign.isChecked(): - - if not self.refStackAvailable: - self.alignRefImage = self.displayedStack - else: - pass - - self.displayedStack = align_stack_iter( - self.displayedStack, - ref_stack_void=False, - ref_stack=self.alignRefImage, - transformation=self.transformType, - method=("previous", "first"), - max_iter=self.alignMaxIter, - ) - - else: - if not self.refStackAvailable: - self.alignRefImage = self.displayedStack - - else: - pass - - self.displayedStack, self.tranform_file = align_stack( - self.displayedStack, - ref_image_void=True, - ref_stack=self.alignRefImage, - transformation=self.transformType, - reference=self.alignReferenceImage, - ) - logger.info("New Tranformation file available") - self.im_stack = self.displayedStack - - def exportAlignTransformation(self): - file_name = QFileDialog().getSaveFileName( - self, "Save Transformation File", "TranformationMatrix.npy", "text file (*.npy)" - ) - if file_name[0]: - np.save(file_name[0], self.tranform_file) - else: - pass - - def importAlignTransformation(self): - file_name = QFileDialog().getOpenFileName(self, "Open Transformation File", " ", "text file (*.npy)") - if file_name[0]: - self.loaded_tranform_file = np.load(file_name[0]) - self.cb_use_tmatFile.setChecked(True) - logger.info("Tranformation File Loaded") - else: - pass - - def loadSplashScreen(self): - self.splash = LoadingScreen() - - px = self.geometry().x() - py = self.geometry().y() - ph = self.geometry().height() - pw = self.geometry().width() - dw = self.splash.width() - dh = self.splash.height() - new_x, new_y = px + (0.5 * pw) - dw, py + (0.5 * ph) - dh - self.splash.setGeometry(new_x, new_y, dw, dh) - - self.splash.show() - - def reloadImageStack(self): - self.isAReload = True - self.load_stack() - - def update_stack(self): - self.displayedStack = self.im_stack - self.crop_to_dim() - - if self.cb_rebin.isChecked(): - self.cb_upscale.setChecked(False) - self.sb_scaling_factor.setEnabled(True) - self.displayedStack = resize_stack(self.displayedStack, scaling_factor=self.sb_scaling_factor.value()) - self.update_stack_info() - - elif self.cb_upscale.isChecked(): - self.cb_rebin.setChecked(False) - self.sb_scaling_factor.setEnabled(True) - self.displayedStack = resize_stack( - self.displayedStack, upscaling=True, scaling_factor=self.sb_scaling_factor.value() - ) - self.update_stack_info() - - if self.cb_remove_outliers.isChecked(): - self.hs_nsigma.setEnabled(True) - nsigma = self.hs_nsigma.value() / 10 - self.displayedStack = remove_hot_pixels(self.displayedStack, NSigma=nsigma) - self.label_nsigma.setText(str(nsigma)) - logger.info(f"Removing Outliers with NSigma {nsigma}") - - elif self.cb_remove_outliers.isChecked() is False: - self.hs_nsigma.setEnabled(False) - - if self.cb_remove_edges.isChecked(): - self.displayedStack = remove_edges(self.displayedStack) - logger.info(f"Removed edges, new shape {self.displayedStack.shape}") - self.update_stack_info() - - if self.cb_remove_bg.isChecked(): - self.hs_bg_threshold.setEnabled(True) - logger.info("Removing background") - bg_threshold = self.hs_bg_threshold.value() - self.label_bg_threshold.setText(str(bg_threshold) + "%") - self.displayedStack = clean_stack(self.displayedStack, auto_bg=False, bg_percentage=bg_threshold) - - elif self.cb_remove_bg.isChecked() is False: - self.hs_bg_threshold.setEnabled(False) - - if self.cb_log.isChecked(): - - self.displayedStack = remove_nan_inf(np.log10(self.displayedStack)) - logger.info("Log Stack is in use") - - if self.cb_smooth.isChecked(): - self.hs_smooth_size.setEnabled(True) - window = self.hs_smooth_size.value() - if window % 2 == 0: - window = +1 - self.smooth_winow_size.setText("Window size: " + str(window)) - self.displayedStack = smoothen(self.displayedStack, w_size=window) - logger.info("Spectrum Smoothening Applied") - - elif self.cb_smooth.isChecked() is False: - self.hs_smooth_size.setEnabled(False) - - if self.cb_norm.isChecked(): - logger.info("Normalizing spectra") - self.displayedStack = normalize(self.displayedStack, norm_point=-1) - - logger.info("Updated image is in use") - - # ImageView - - def view_stack(self): - - if not self.im_stack.ndim == 3: - raise ValueError("stack should be an ndarray with ndim == 3") - else: - self.update_stack() - # self.StackUpdateThread() - - try: - self.image_view.removeItem(self.image_roi_math) - except Exception: - pass - - (self.dim1, self.dim2, self.dim3) = self.displayedStack.shape - self.image_view.setImage(self.displayedStack) - self.image_view.ui.menuBtn.hide() - self.image_view.ui.roiBtn.hide() - self.image_view.setPredefinedGradient("viridis") - self.image_view.setCurrentIndex(self.dim1 // 2) - if len(self.energy) == 0: - self.energy = np.arange(self.z1, self.z2) * 10 - logger.info("Arbitary X-axis used in the plot for XANES") - self.sz = np.max( - [int(self.dim2 * 0.1), int(self.dim3 * 0.1)] - ) # size of the roi set to be 10% of the image area - - self.stack_center = self.energy[len(self.energy) // 2] - self.stack_width = (self.energy.max() - self.energy.min()) // 10 - self.spec_roi = pg.LinearRegionItem( - values=(self.stack_center - self.stack_width, self.stack_center + self.stack_width) - ) - - # a second optional ROI for calculations follow - self.image_roi_math = pg.PolyLineROI( - [[0, 0], [0, self.sz], [self.sz, self.sz], [self.sz, 0]], - pos=(int(self.dim3 // 3), int(self.dim2 // 3)), - pen="r", - closed=True, - removable=True, - ) - - self.spec_roi_math = pg.LinearRegionItem( - values=(self.stack_center - self.stack_width - 10, self.stack_center + self.stack_width - 10), - pen="r", - brush=QtGui.QColor(0, 255, 200, 50), - ) - self.spec_lo_m_idx = self.spec_hi_m_idx = 0 - - self.setImageROI() - self.update_spectrum() - self.update_image_roi() - - if not self.isAReload: - # image connections - self.image_view.mousePressEvent = self.getPointSpectrum - self.spec_roi.sigRegionChanged.connect(self.update_image_roi) - self.spec_roi_math.sigRegionChangeFinished.connect(self.spec_roi_calc) - self.pb_apply_spec_calc.clicked.connect(self.spec_roi_calc) - self.rb_math_roi.clicked.connect(self.update_spectrum) - self.pb_add_roi_2.clicked.connect(self.math_img_roi_flag) - self.image_roi_math.sigRegionChangeFinished.connect(self.image_roi_calc) - self.pb_apply_img_calc.clicked.connect(self.image_roi_calc) - - [ - rbs.clicked.connect(self.setImageROI) - for rbs in [self.rb_poly_roi, self.rb_elli_roi, self.rb_rect_roi, self.rb_line_roi, self.rb_circle_roi] - ] - - def disconnectImageActions(self): - for btns in [self.pb_apply_spec_calc, self.rb_math_roi, self.pb_add_roi_2, self.pb_apply_img_calc]: - try: - btns.disconnect() - except Exception: - pass - - def select_elist(self): - self.energyFileChooser() - self.efileLoader() - self.energyUnitCheck() - self.view_stack() - - def efileLoader(self): - - if self.efilePath: - - if str(self.efilePath).endswith("log_tiff.txt"): - self.energy = energy_from_logfile(logfile=str(self.efilePath)) - logger.info("Log file from pyxrf processing") - - else: - self.energy = np.loadtxt(str(self.efilePath)) - self.change_color_on_load(self.pb_elist_xanes) - logger.info("Energy file loaded") - - else: - self.statusbar_main.showMessage("No Energy List Selected, Setting Arbitary Axis") - self.energy = np.arange(self.im_stack.shape[0]) - logger.info("Arbitary Energy Axis") - - # assert len(self.energy) == self.dim1, "Number of Energy Points is not equal to stack length" - - def energyUnitCheck(self): - - if np.max(self.energy) < 100: - self.cb_kev_flag.setChecked(True) - self.energy *= 1000 - - else: - self.cb_kev_flag.setChecked(False) - - def select_ref_file(self): - self.pb_xanes_fit.setEnabled(True) - self.ref_names = [] - file_name = QFileDialog().getOpenFileName(self, "Open reference file", "", "text file (*.txt *.nor)") - if file_name[0]: - if file_name[0].endswith(".nor"): - self.refs, self.ref_names = create_df_from_nor_try2(athenafile=str(file_name[0])) - self.change_color_on_load(self.pb_ref_xanes) - - elif file_name[0].endswith(".txt"): - self.refs = pd.read_csv(str(file_name[0]), header=None, delim_whitespace=True) - self.change_color_on_load(self.pb_ref_xanes) - - else: - logger.error("No file selected") - pass - - self.plt_xanes_refs() - - def plt_xanes_refs(self): - - try: - self.ref_plot.close() - except Exception: - pass - - self.ref_plot = plot(title="Reference Standards") - self.ref_plot.setLabel("bottom", "Energy") - self.ref_plot.setLabel("left", "Intensity") - self.ref_plot.addLegend() - - for n in range(np.shape(self.refs)[1]): - - if not n == 0: - self.ref_plot.plot( - self.refs.values[:, 0], - self.refs.values[:, n], - pen=pg.mkPen(self.plt_colors[n - 1], width=self.plotWidth), - name=self.ref_names[n], - ) - - def getPointSpectrum(self, event): - if event.type() == QtCore.QEvent.MouseButtonDblClick: - if event.button() == QtCore.Qt.LeftButton: - self.xpixel = int(self.image_view.view.mapSceneToView(event.pos()).x()) - 1 - zlim, ylim, xlim = self.displayedStack.shape - - if self.xpixel > xlim: - self.xpixel = xlim - 1 - - self.ypixel = int(self.image_view.view.mapSceneToView(event.pos()).y()) - 1 - if self.ypixel > ylim: - self.ypixel = ylim - 1 - self.spectrum_view.addLegend() - self.point_spectrum = self.displayedStack[:, self.ypixel, self.xpixel] - self.spectrum_view.plot( - self.xdata, - self.point_spectrum, - clear=True, - pen=pg.mkPen(pg.mkColor(0, 0, 255, 255), width=self.plotWidth), - symbol="o", - symbolSize=6, - symbolBrush="r", - name=f"Point Spectrum; x= {self.xpixel}, y= {self.ypixel}", - ) - - self.spectrum_view.addItem(self.spec_roi) - - self.statusbar_main.showMessage(f"{self.xpixel} and {self.ypixel}") - - def setImageROI(self): - - self.lineROI = pg.LineSegmentROI([[int(self.dim3 // 2), int(self.dim2 // 2)], [self.sz, self.sz]], pen="r") - - self.rectROI = pg.RectROI( - [int(self.dim3 // 2), int(self.dim2 // 2)], - [self.sz, self.sz], - pen="w", - maxBounds=QtCore.QRectF(0, 0, self.dim3, self.dim2), - ) - - self.rectROI.addTranslateHandle([0, 0], [2, 2]) - self.rectROI.addRotateHandle([0, 1], [2, 2]) - - self.ellipseROI = pg.EllipseROI( - [int(self.dim3 // 2), int(self.dim2 // 2)], - [self.sz, self.sz], - pen="w", - maxBounds=QtCore.QRectF(0, 0, self.dim3, self.dim2), - ) - - self.circleROI = pg.CircleROI( - [int(self.dim3 // 2), int(self.dim2 // 2)], - [self.sz, self.sz], - pen="w", - maxBounds=QtCore.QRectF(0, 0, self.dim3, self.dim2), - ) # pos and size - - self.polyLineROI = pg.PolyLineROI( - [[0, 0], [0, self.sz], [self.sz, self.sz], [self.sz, 0]], - pos=(int(self.dim3 // 2), int(self.dim2 // 2)), - maxBounds=QtCore.QRectF(0, 0, self.dim3, self.dim2), - closed=True, - removable=True, - ) - - self.rois = { - "rb_line_roi": self.lineROI, - "rb_rect_roi": self.rectROI, - "rb_circle_roi": self.circleROI, - "rb_elli_roi": self.ellipseROI, - "rb_poly_roi": self.polyLineROI, - } - - button_name = self.sender() - - if button_name.objectName() in self.rois.keys(): - self.roi_preference = button_name.objectName() - - else: - self.roi_preference = "rb_rect_roi" # default - - try: - self.image_view.removeItem(self.image_roi) - - except Exception: - pass - - # ROI settings for image, used polyline roi with non rectangular shape - - self.image_roi = self.rois[self.roi_preference] - self.image_view.addItem(self.image_roi) - self.image_roi.sigRegionChanged.connect(self.update_spectrum) - - def replot_image(self): - self.update_stack() - self.update_spectrum() - self.update_image_roi() - - def update_spec_roi_values(self): - self.stack_center = int(self.energy[len(self.energy) // 2]) - self.stack_width = int((self.energy.max() - self.energy.min()) * 0.05) - self.spec_roi.setBounds([self.xdata[0], self.xdata[-1]]) # if want to set bounds for the spec roi - self.spec_roi_math.setBounds([self.xdata[0], self.xdata[-1]]) - - def update_spectrum(self): - - # set x-axis values; array taken from energy values, if clipped z box values will update the array - self.xdata = self.energy[self.sb_zrange1.value() : self.sb_zrange2.value()] - - # get the cropped stack from ROI region; pyqtgraph function is used - self.roi_img_stk = self.image_roi.getArrayRegion( - self.displayedStack, self.image_view.imageItem, axes=(1, 2) - ) - - posx, posy = self.image_roi.pos() - self.le_roi.setText(str(int(posx)) + ":" + str(int(posy))) - - # display the ROI features in the line edit boxes - if self.roi_img_stk.ndim == 3: - sizex, sizey = self.roi_img_stk.shape[1], self.roi_img_stk.shape[2] - self.le_roi_size.setText(str(sizex) + "," + str(sizey)) - self.mean_spectra = get_mean_spectra(self.roi_img_stk) - - elif self.roi_img_stk.ndim == 2: - sizex, sizey = self.roi_img_stk.shape[0], self.roi_img_stk.shape[1] - self.le_roi_size.setText(str(sizex) + "," + str(sizey)) - self.mean_spectra = self.roi_img_stk.mean(-1) - - self.spectrum_view.addLegend() - - try: - self.spectrum_view.plot( - self.xdata, - self.mean_spectra, - pen=pg.mkPen(pg.mkColor(5, 255, 5, 255), width=self.plotWidth), - clear=True, - symbol="o", - symbolSize=6, - symbolBrush="r", - name="ROI Spectrum", - ) - except Exception: - self.spectrum_view.plot( - self.mean_spectra, - clear=True, - pen=pg.mkPen(pg.mkColor(5, 255, 5, 255), width=self.plotWidth), - symbol="o", - symbolSize=6, - symbolBrush="r", - name="ROI Spectrum", - ) - - if self.energy[-1] > 1000: - self.e_unit = "eV" - else: - self.e_unit = "keV" - - self.spectrum_view.setLabel("bottom", "Energy", self.e_unit) - self.spectrum_view.setLabel("left", "Intensity", "A.U.") - self.spectrum_view.addItem(self.spec_roi) - self.update_spec_roi_values() - self.math_roi_flag() - - def update_image_roi(self): - self.spec_lo, self.spec_hi = self.spec_roi.getRegion() - self.spec_lo_idx = (np.abs(self.energy - self.spec_lo)).argmin() - self.spec_hi_idx = (np.abs(self.energy - self.spec_hi)).argmin() - self.le_spec_roi.setText(str(int(self.spec_lo)) + ":" + str(int(self.spec_hi))) - self.le_spec_roi_size.setText(str(int(self.spec_hi - self.spec_lo))) - self.update_spec_roi_values() - self.stackIndexToNames() - - try: - if int(self.spec_lo_idx) == int(self.spec_hi_idx): - self.disp_img = self.displayedStack[int(self.spec_hi_idx), :, :] - - else: - self.disp_img = self.displayedStack[int(self.spec_lo_idx) : int(self.spec_hi_idx), :, :].mean(0) - - self.image_view.setImage(self.disp_img) - self.statusbar_main.showMessage(f"Image Display is {self.corrImg1}") - except Exception: - logger.warning("Indices are out of range; Image cannot be created") - pass - - def set_spec_roi(self): - self.spec_lo_, self.spec_hi_ = int(self.sb_roi_spec_s.value()), int(self.sb_roi_spec_e.value()) - self.spec_lo_idx_ = (np.abs(self.energy - self.spec_lo_)).argmin() - self.spec_hi_idx_ = (np.abs(self.energy - self.spec_hi_)).argmin() - self.spec_roi.setRegion((self.xdata[self.spec_lo_idx_], self.xdata[self.spec_hi_idx_])) - self.update_image_roi() - - def math_roi_flag(self): - if self.rb_math_roi.isChecked(): - self.rb_math_roi.setStyleSheet("color : green") - self.spectrum_view.addItem(self.spec_roi_math) - else: - self.spectrum_view.removeItem(self.spec_roi_math) - - def spec_roi_calc(self): - - self.spec_lo_m, self.spec_hi_m = self.spec_roi_math.getRegion() - self.spec_lo_m_idx = (np.abs(self.energy - self.spec_lo_m)).argmin() - self.spec_hi_m_idx = (np.abs(self.energy - self.spec_hi_m)).argmin() - - if int(self.spec_lo_idx) == int(self.spec_hi_idx): - self.img1 = self.displayedStack[int(self.spec_hi_idx), :, :] - - else: - self.img1 = self.displayedStack[int(self.spec_lo_idx) : int(self.spec_hi_idx), :, :].mean(0) - - if int(self.spec_lo_m_idx) == int(self.spec_hi_m_idx): - self.img2 = self.displayedStack[int(self.spec_hi_m_idx), :, :] - - else: - self.img2 = self.displayedStack[int(self.spec_lo_m_idx) : int(self.spec_hi_m_idx), :, :].mean(0) - - if self.cb_roi_operation.currentText() == "Correlation Plot": - self.correlation_plot() - - else: - calc = {"Divide": np.divide, "Subtract": np.subtract, "Add": np.add} - self.disp_img = remove_nan_inf(calc[self.cb_roi_operation.currentText()](self.img1, self.img2)) - self.image_view.setImage(self.disp_img) - - def math_img_roi_flag(self): - - button_name = self.sender().text() - logger.info(f"{button_name}") - if button_name == "Add ROI_2": - self.image_view.addItem(self.image_roi_math) - self.pb_add_roi_2.setText("Remove ROI_2") - self.image_roi2_flag = 1 - elif button_name == "Remove ROI_2": - self.image_view.removeItem(self.image_roi_math) - self.pb_add_roi_2.setText("Add ROI_2") - self.image_roi2_flag = 0 - - else: - pass - logger.error("Unknown signal for second ROI") - - def image_roi_calc(self): - - if self.image_roi2_flag == 1: - self.calc = {"Divide": np.divide, "Subtract": np.subtract, "Add": np.add} - self.update_spec_image_roi() - else: - logger.error("No ROI2 found") - return - - def update_spec_image_roi(self): - - self.math_roi_reg = self.image_roi_math.getArrayRegion( - self.displayedStack, self.image_view.imageItem, axes=(1, 2) - ) - if self.math_roi_reg.ndim == 3: - - self.math_roi_spectra = get_mean_spectra(self.math_roi_reg) - - elif self.roi_img_stk.ndim == 2: - self.math_roi_spectra = self.math_roi_reg.mean(-1) - - if self.cb_img_roi_action.currentText() in self.calc.keys(): - - calc_spec = self.calc[self.cb_img_roi_action.currentText()](self.mean_spectra, self.math_roi_spectra) - self.spectrum_view.addLegend() - self.spectrum_view.plot( - self.xdata, - calc_spec, - clear=True, - pen=pg.mkPen("m", width=2), - name=self.cb_img_roi_action.currentText() + "ed", - ) - self.spectrum_view.plot(self.xdata, self.math_roi_spectra, pen=pg.mkPen("y", width=2), name="ROI2") - self.spectrum_view.plot(self.xdata, self.mean_spectra, pen=pg.mkPen("g", width=2), name="ROI1") - - elif self.cb_img_roi_action.currentText() == "Compare": - self.spectrum_view.plot( - self.xdata, self.math_roi_spectra, pen=pg.mkPen("y", width=2), clear=True, name="ROI2" - ) - self.spectrum_view.plot(self.xdata, self.mean_spectra, pen=pg.mkPen("g", width=2), name="ROI1") - - self.spectrum_view.addItem(self.spec_roi) - - def displayStackInfo(self): - - try: - - if isinstance(self.file_name, list): - info = f"Folder; {os.path.dirname(self.file_name[0])} \n" - for n, name in enumerate(self.file_name): - info += f"{n}: {os.path.basename(name)} \n" - - # info = f'Stack order; {[name for name in enumerate(self.file_name)]}' - else: - info = f"Stack; {self.file_name}" - - self.infoWindow = StackInfo(str(info)) - self.infoWindow.show() - - except AttributeError: - self.statusbar_main.showMessage("Warning: No Image Data Loaded") - - def stackIndexToNames(self): - # create list of tiff file names for virtutal stack for plot axes - self.elemFileName = [] - - if isinstance(self.file_name, list): - for name in self.file_name: - self.elemFileName.append(os.path.basename(name).split(".")[0]) - - logger.info(f" Virtual Stack - list of image names; {self.elemFileName}") - - # if the roi focus on one frame, Note that this slicing excludes the last index - if int(self.spec_lo_idx) == int(self.spec_hi_idx): - self.corrImg1 = str(self.elemFileName[int(self.spec_lo_idx)]) - else: - self.corrImg1 = self.elemFileName[int(self.spec_lo_idx) : int(self.spec_hi_idx)] - if len(self.corrImg1) > 1: - self.corrImg1 = f"Sum of {self.corrImg1} " - - if int(self.spec_lo_m_idx) == int(self.spec_hi_m_idx): - self.corrImg2 = str(self.elemFileName[int(self.spec_lo_m_idx)]) - - else: - self.corrImg2 = self.elemFileName[int(self.spec_lo_m_idx) : int(self.spec_hi_m_idx)] - - if len(self.corrImg2) > 1: - self.corrImg2 = f"Sum of {self.corrImg2}" - - logger.info( - f"Correlation stack {int(self.spec_lo_idx)}:{int(self.spec_hi_idx)} with " - f"{int(self.spec_lo_m_idx)}:{int(self.spec_hi_m_idx)}" - ) - - logger.info(f" Virtual Stack; corrlation plot of {self.corrImg1} vs {self.corrImg2}") - else: - self.corrImg1 = ( - f" Sum of {os.path.basename(self.file_name).split('.')[0]}_{int(self.spec_lo_idx)} " - f"to {int(self.spec_hi_idx)}" - ) - self.corrImg2 = ( - f" Sum of {os.path.basename(self.file_name).split('.')[0]}_{int(self.spec_lo_m_idx)} " - f"to {int(self.spec_hi_m_idx)}" - ) - # logger.info(f" corrlation plot of {self.corrImg1} vs {self.corrImg2}") - - def correlation_plot(self): - self.stackIndexToNames() - - self.statusbar_main.showMessage(f"Correlation of {self.corrImg1} with {self.corrImg2}") - - if self.rb_roiRegionOnly.isChecked(): - self.roi_mask = self.image_roi.getArrayRegion( - self.displayedStack, self.image_view.imageItem, axes=(1, 2) - ) - self.roi_img1 = np.mean(self.roi_mask[int(self.spec_lo_idx) : int(self.spec_hi_idx)], axis=0) - self.roi_img2 = np.mean(self.roi_mask[int(self.spec_lo_m_idx) : int(self.spec_hi_m_idx)], axis=0) - self.scatter_window = ScatterPlot( - self.roi_img1, self.roi_img2, (str(self.corrImg1), str(self.corrImg2)) - ) - - else: - - self.scatter_window = ScatterPlot(self.img1, self.img2, (str(self.corrImg1), str(self.corrImg2))) - - # ph = self.geometry().height() - # pw = self.geometry().width() - # px = self.geometry().x() - # py = self.geometry().y() - # dw = self.scatter_window.width() - # dh = self.scatter_window.height() - # self.scatter_window.setGeometry(px+0.65*pw, py + ph - 2*dh-5, dw, dh) - self.scatter_window.show() - - def plotCorrelationsAllCombinations(self): - - self.stackIndexToNames() - allElemCombNum = list(combinations(np.arange(len(self.elemFileName)), 2)) - - self.scW1 = self.scW2 = self.scW3 = self.scW4 = self.scW5 = None - self.scW6 = self.scW7 = self.scW8 = self.scW9 = self.scW10 = None - - self.scWindowList = [ - self.scW1, - self.scW2, - self.scW3, - self.scW4, - self.scW5, - self.scW6, - self.scW7, - self.scW8, - self.scW9, - self.scW10, - ] - self.scWindowDict = { - 1: self.scW1, - 2: self.scW2, - 3: self.scW3, - 4: self.scW4, - 5: self.scW5, - 6: self.scW6, - 7: self.scW7, - 8: self.scW8, - 9: self.scW9, - 10: self.scW10, - } - - if len(allElemCombNum) > len(self.scWindowDict): - - reply = QMessageBox.warning( - self, - "Plot Window Limit", - f"The number of combination exceeds " - f"maxiumum number of " - f"plot windows. First {len(self.scWindowDict)} " - f"combinations will be plotted. \n Proceed?", - QMessageBox.Yes | QMessageBox.No, - QMessageBox.No, - ) - - if reply == QMessageBox.Yes: - - for i, pair in enumerate(allElemCombNum): - im1 = self.displayedStack[pair[0]] - im2 = self.displayedStack[pair[1]] - im1Name = self.elemFileName[pair[0]] - im2Name = self.elemFileName[pair[1]] - - self.scWindowDict[i] = ScatterPlot(im1, im2, (str(im1Name), str(im2Name))) - self.scWindowDict[i].show() - - if reply == QMessageBox.No: - return - - def getROIMask(self): - self.roi_mask = self.image_roi.getArrayRegion(self.displayedStack, self.image_view.imageItem, axes=(1, 2)) - self.newWindow = singleStackViewer(self.roi_mask) - self.newWindow.show() - - def save_stack(self, method="raw"): - - # self.update_stack() - file_name = QFileDialog().getSaveFileName( - self, "Save image data", "image_data.tiff", "image file(*tiff *tif )" - ) - if file_name[0]: - if method == "raw": - - tf.imsave(str(file_name[0]), self.displayedStack) - logger.info(f"Updated Image Saved: {str(file_name[0])}") - self.statusbar_main.showMessage(f"Updated Image Saved: {str(file_name[0])}") - elif method == "sum": - tf.imsave(str(file_name[0]), np.sum(self.displayedStack, axis=0)) - - elif method == "mean": - tf.imsave(str(file_name[0]), np.mean(self.displayedStack, axis=0)) - - else: - self.statusbar_main.showMessage("Saving cancelled") - logger.info(f"Save failed: {str(file_name[0])}") - pass - - def save_disp_img(self): - file_name = QFileDialog().getSaveFileName(self, "Save image data", "image.tiff", "image file(*tiff *tif )") - if file_name[0]: - tf.imsave(str(file_name[0]), self.disp_img) - self.statusbar_main.showMessage(f"Image Saved to {str(file_name[0])}") - logger.info(f"Updated Image Saved: {str(file_name[0])}") - - else: - logger.error("No file to save") - self.statusbar_main.showMessage("Saving cancelled") - pass - - def getLivePlotData(self): - try: - - data = np.squeeze([c.getData() for c in self.spectrum_view.plotItem.curves]) - # print(np.shape(data)) - if data.ndim == 2: - self.mu_ = data[1] - self.e_ = data[0] - elif data.ndim == 3: - e_mu = data[0, :, :] - self.mu_ = e_mu[1] - self.e_ = e_mu[0] - - else: - logger.error(f" Data shape of {data.ndim} is not supported") - pass - except AttributeError: - logger.error("No data loaded") - pass - - def addSpectrumToCollector(self): - self.getLivePlotData() - self.spectrum_view_collect.plot(self.e_, self.mu_, name="ROI Spectrum") - self.spectrum_view_collect.setLabel("bottom", "Energy", self.e_unit) - self.spectrum_view_collect.setLabel("left", "Intensity", "A.U.") - - def findEo(self): - try: - self.getLivePlotData() - e0_init = self.e_[np.argmax(np.gradient(self.mu_))] - self.dsb_norm_Eo.setValue(e0_init) - - except AttributeError: - logger.error("No data loaded") - pass - - def initNormVals(self): - self.getLivePlotData() - e0_init = self.e_[np.argmax(np.gradient(self.mu_))] - pre1, pre2, post1, post2 = xanesNormalization( - self.e_, self.mu_, e0=e0_init, step=None, nnorm=1, nvict=0, guess=True - ) - self.dsb_norm_pre1.setValue(pre1) - self.dsb_norm_pre2.setValue(pre2) - self.dsb_norm_post1.setValue(post1) - self.dsb_norm_post2.setValue(post2) - self.dsb_norm_Eo.setValue(e0_init) - - def getNormParams(self): - self.getLivePlotData() - eo_ = self.dsb_norm_Eo.value() - pre1_, pre2_ = self.dsb_norm_pre1.value(), self.dsb_norm_pre2.value() - norm1_, norm2_ = self.dsb_norm_post1.value(), self.dsb_norm_post2.value() - norm_order = self.sb_norm_order.value() - - return eo_, pre1_, pre2_, norm1_, norm2_, norm_order - - def exportNormParams(self): - self.xanesNormParam = {} - eo_, pre1_, pre2_, norm1_, norm2_, norm_order = self.getNormParams() - self.xanesNormParam["E0"] = eo_ - self.xanesNormParam["pre1"] = pre1_ - self.xanesNormParam["pre2"] = pre2_ - self.xanesNormParam["post1"] = norm1_ - self.xanesNormParam["post2"] = norm2_ - self.xanesNormParam["norm_order"] = norm_order - - file_name = QtWidgets.QFileDialog().getSaveFileName( - self, "Save XANES Norm Params", "xanes_norm_params.csv", "csv file(*csv)" - ) - - if file_name[0]: - - pd.DataFrame(self.xanesNormParam, index=[0]).to_csv(file_name[0]) - - else: - pass - - def importNormParams(self): - - file_name = QtWidgets.QFileDialog().getOpenFileName( - self, "Open a XANES Norm File", "", "csv file(*csv);;all_files (*)" - ) - - if file_name[0]: - xanesNormParam = pd.read_csv(file_name[0]) - self.dsb_norm_Eo.setValue(xanesNormParam["E0"]) - self.dsb_norm_pre1.setValue(xanesNormParam["pre1"]) - self.dsb_norm_pre2.setValue(xanesNormParam["pre2"]) - self.dsb_norm_post1.setValue(xanesNormParam["post1"]) - self.dsb_norm_post2.setValue(xanesNormParam["post2"]) - self.sb_norm_order.setValue(xanesNormParam["norm_order"]) - - def nomalizeLiveSpec(self): - eo_, pre1_, pre2_, norm1_, norm2_, norm_order = self.getNormParams() - self.spectrum_view.clear() - - pre_line, post_line, normXANES = xanesNormalization( - self.e_, - self.mu_, - e0=eo_, - step=None, - nnorm=norm_order, - nvict=0, - pre1=pre1_, - pre2=pre2_, - norm1=norm1_, - norm2=norm2_, - ) - - names = np.array(("Spectrum", "Pre", "Post")) - data_array = np.array((self.mu_, pre_line, post_line)) - colors = np.array(("c", "r", "m")) - - for data, clr, name in zip(data_array, colors, names): - self.spectrum_view.plot(self.e_, data, pen=pg.mkPen(clr, width=self.plotWidth), name=name) - - self.spectrum_view_norm.plot( - self.e_, normXANES, clear=True, pen=pg.mkPen(self.plt_colors[-1], width=self.plotWidth) - ) - self.spectrum_view_norm.setLabel("bottom", "Energy", self.e_unit) - self.spectrum_view_norm.setLabel("left", "Norm. Intensity", "A.U.") - - def normalizeStack(self): - self.getLivePlotData() - eo_, pre1_, pre2_, norm1_, norm2_, norm_order = self.getNormParams() - - self.im_stack = self.displayedStack = xanesNormStack( - self.e_, - self.displayedStack, - e0=eo_, - step=None, - nnorm=norm_order, - nvict=0, - pre1=pre1_, - pre2=pre2_, - norm1=norm1_, - norm2=norm2_, - ) - # self.im_stack = self.displayedStack - - def transposeStack(self): - self.im_stack = self.displayedStack = np.transpose(self.displayedStack, (2, 1, 0)) - - def swapStackXY(self): - self.im_stack = self.displayedStack = np.transpose(self.displayedStack, (0, 2, 1)) - - def removeROIBGStack(self): - self.displayedStack = subtractBackground(self.displayedStack, self.mean_spectra) - - def resetCollectorSpec(self): - pass - - def saveCollectorPlot(self): - exporter = pg.exporters.CSVExporter(self.spectrum_view_collect.plotItem) - exporter.parameters()["columnMode"] = "(x,y,y,y) for all plots" - file_name = QFileDialog().getSaveFileName(self, "save spectra", "", "spectra (*csv)") - if file_name[0]: - exporter.export(str(file_name[0]) + ".csv") - else: - self.statusbar_main.showMessage("Saving cancelled") - pass - - def save_disp_spec(self): - - exporter = pg.exporters.CSVExporter(self.spectrum_view.plotItem) - exporter.parameters()["columnMode"] = "(x,y,y,y) for all plots" - file_name = QFileDialog().getSaveFileName(self, "save spectrum", "", "spectra (*csv)") - if file_name[0]: - exporter.export(str(file_name[0]) + ".csv") - else: - self.statusbar_main.showMessage("Saving cancelled") - pass - - def saveEnergyList(self): - file_name = QFileDialog().getSaveFileName(self, "save energy list", "energy_list.txt", "text file (*txt)") - if file_name[0]: - np.savetxt(file_name[0], self.xdata, fmt="%.4f") - else: - pass - - def pca_scree_(self): - logger.info("Process started..") - self.update_stack() - pca_scree(self.displayedStack) - logger.info("Process complete") - - def calc_comp_(self): - - logger.info("Process started..") - - # self.update_stack() - n_components = self.sb_ncomp.value() - method_ = self.cb_comp_method.currentText() - - ims, comp_spec, decon_spec, decomp_map = decompose_stack( - self.displayedStack, decompose_method=method_, n_components_=n_components - ) - - self._new_window3 = ComponentViewer(ims, self.xdata, comp_spec, decon_spec, decomp_map) - self._new_window3.show() - - logger.info("Process complete") - - def kmeans_elbow(self): - logger.info("Process started..") - # self.update_stack() - - with pg.BusyCursor(): - try: - kmeans_variance(self.displayedStack) - logger.info("Process complete") - except OverflowError: - pass - logger.error("Overflow Error, values are too long") - - def kmeans_elbow_Thread(self): - # Pass the function to execute - worker = Worker(self.kmeans_elbow) # Any other args, kwargs are passed to the run function - worker.signals.result.connect(self.print_output) - worker.signals.finished.connect(self.thread_complete) - # Execute - self.threadpool.start(worker) - - def clustering_(self): - - logger.info("Process started..") - # self.update_stack() - method_ = self.cb_clust_method.currentText() - - decon_images, X_cluster, decon_spectra = cluster_stack( - self.displayedStack, - method=method_, - n_clusters_=self.sb_ncluster.value(), - decomposed=False, - decompose_method=self.cb_comp_method.currentText(), - decompose_comp=self.sb_ncomp.value(), - ) - - self._new_window4 = ClusterViewer(decon_images, self.xdata, X_cluster, decon_spectra) - self._new_window4.show() - - logger.info("Process complete") - - def change_color_on_load(self, button_name): - button_name.setStyleSheet("background-color : rgb(0,150,0);" "color: rgb(255,255,255)") - - def energyFileChooser(self): - file_name = QFileDialog().getOpenFileName(self, "Open energy list", "", "text file (*.txt)") - self.efilePath = file_name[0] - - def fast_xanes_fitting(self): - - self._new_window5 = XANESViewer(self.displayedStack, self.xdata, self.refs, self.ref_names) - self._new_window5.show() - - # Thread Signals - - def print_output(self, s): - print(s) - - def thread_complete(self): - print("THREAD COMPLETE!") - - def closeEvent(self, event): - reply = QMessageBox.question( - self, - "Window Close", - "Are you sure you want to close?", - QMessageBox.Yes | QMessageBox.No, - QMessageBox.No, - ) - - if reply == QMessageBox.Yes: - event.accept() - QApplication.closeAllWindows() - else: - event.ignore() - - -class WorkerSignals(QObject): - """ - Defines the signals available from a running worker thread. - Supported signals are: - - finished: No data - - error:`tuple` (exctype, value, traceback.format_exc() ) - - result: `object` data returned from processing, anything - - progress: `tuple` indicating progress metadata - """ - - start = pyqtSignal() - finished = pyqtSignal() - error = pyqtSignal(tuple) - result = pyqtSignal(object) - - -class Worker(QRunnable): - """ - Worker thread - Inherits from QRunnable to handler worker thread setup, signals and wrap-up. - """ - - def __init__(self, fn, *args, **kwargs): - super(Worker, self).__init__() - # Store constructor arguments (re-used for processing) - self.fn = fn - self.args = args - self.kwargs = kwargs - self.signals = WorkerSignals() - - @pyqtSlot() - def run(self): - """ - Initialise the runner function with passed args, kwargs. - """ - # Retrieve args/kwargs here; and fire processing using them - self.signals.start.emit() - try: - result = self.fn(*self.args, **self.kwargs) - except Exception: - traceback.print_exc() - exctype, value = sys.exc_info()[:2] - self.signals.error.emit((exctype, value, traceback.format_exc())) - else: - self.signals.result.emit(result) # Return the result of the processing - finally: - self.signals.finished.emit() # Done - - -class singleStackViewer(QtWidgets.QMainWindow): - def __init__(self, img_stack, gradient="viridis"): - super(singleStackViewer, self).__init__() - - # Load the UI Page - uic.loadUi(os.path.join(ui_path, "uis/singleStackView.ui"), self) - - self.image_view.ui.menuBtn.hide() - self.image_view.ui.roiBtn.hide() - - self.img_stack = img_stack - self.gradient = gradient - self.image_view.setPredefinedGradient(gradient) - - if self.img_stack.ndim == 3: - self.dim1, self.dim3, self.dim2 = img_stack.shape - elif self.img_stack.ndim == 2: - self.dim3, self.dim2 = img_stack.shape - self.dim1 = 1 - self.hs_img_stack.setMaximum(self.dim1 - 1) - self.hs_img_stack.setValue(np.round(self.dim1 / 2)) - self.displayStack() - - # connections - self.hs_img_stack.valueChanged.connect(self.displayStack) - self.actionSave.triggered.connect(self.saveImageStackAsTIFF) - - def displayStack(self): - im_index = self.hs_img_stack.value() - if self.img_stack.ndim == 2: - self.image_view.setImage(self.img_stack) - else: - self.image_view.setImage(self.img_stack[im_index]) - self.label_img_count.setText(f"{im_index + 1}/{self.dim1}") - - def saveImageStackAsTIFF(self): - file_name = QFileDialog().getSaveFileName(self, "", "", "*.tiff;;*.tif") - if file_name[0]: - if self.img_stack.ndim == 3: - tf.imsave(str(file_name[0]), np.float32(self.img_stack.transpose(0, 2, 1))) - elif self.img_stack.ndim == 2: - tf.imsave(str(file_name[0]), np.float32(self.img_stack.T)) - else: - pass - - -class ComponentViewer(QtWidgets.QMainWindow): - def __init__(self, comp_stack, energy, comp_spectra, decon_spectra, decomp_map): - super(ComponentViewer, self).__init__() - - # Load the UI Page - uic.loadUi(os.path.join(ui_path, "uis/ComponentView.ui"), self) - self.centralwidget.setStyleSheet(open(os.path.join(ui_path, "css/defaultStyle.css")).read()) - - self.comp_stack = comp_stack - self.energy = energy - self.comp_spectra = comp_spectra - self.decon_spectra = decon_spectra - self.decomp_map = decomp_map - - (self.dim1, self.dim3, self.dim2) = self.comp_stack.shape - self.hs_comp_number.setMaximum(self.dim1 - 1) - - self.image_view.setImage(self.comp_stack) - self.image_view.setPredefinedGradient("viridis") - self.image_view.ui.menuBtn.hide() - self.image_view.ui.roiBtn.hide() - - self.image_view2.setImage(self.decomp_map) - self.image_view2.setPredefinedGradient("bipolar") - self.image_view2.ui.menuBtn.hide() - self.image_view2.ui.roiBtn.hide() - - # connection - self.update_image() - self.pb_show_all.clicked.connect(self.show_all_spec) - self.hs_comp_number.valueChanged.connect(self.update_image) - self.actionSave.triggered.connect(self.save_comp_data) - self.pb_openScatterPlot.clicked.connect(self.openScatterPlot) - self.pb_showMultiColor.clicked.connect(self.generateMultiColorView) - - def update_image(self): - im_index = self.hs_comp_number.value() - self.spectrum_view.setLabel("bottom", "Energy") - self.spectrum_view.setLabel("left", "Intensity", "A.U.") - self.spectrum_view.plot(self.energy, self.decon_spectra[:, im_index], clear=True) - self.component_view.setLabel("bottom", "Energy") - self.component_view.setLabel("left", "Weight", "A.U.") - self.component_view.plot(self.energy, self.comp_spectra[:, im_index], clear=True) - self.label_comp_number.setText(f"{im_index + 1}/{self.dim1}") - # self.image_view.setCurrentIndex(im_index-1) - self.image_view.setImage(self.comp_stack[im_index]) - - def openScatterPlot(self): - self.scatter_window = ComponentScatterPlot(self.comp_stack, self.comp_spectra) - - # ph = self.geometry().height() - # pw = self.geometry().width() - # px = self.geometry().x() - # py = self.geometry().y() - # dw = self.scatter_window.width() - # dh = self.scatter_window.height() - # self.scatter_window.setGeometry(px+0.65*pw, py + ph - 2*dh-5, dw, dh) - self.scatter_window.show() - - def show_all_spec(self): - self.spectrum_view.clear() - self.plt_colors = ["g", "b", "r", "c", "m", "y", "w"] * 10 - offsets = np.arange(0, 2, 0.2) - self.spectrum_view.addLegend() - for ii in range(self.decon_spectra.shape[1]): - self.spectrum_view.plot( - self.energy, - (self.decon_spectra[:, ii] / self.decon_spectra[:, ii].max()) + offsets[ii], - pen=self.plt_colors[ii], - name="component" + str(ii + 1), - ) - - def save_comp_data(self): - file_name = QFileDialog().getSaveFileName(self, "", "", "data(*tiff *tif *txt *png )") - if file_name[0]: - tf.imsave( - str(file_name[0]) + "_components.tiff", np.float32(self.comp_stack.transpose(0, 2, 1)), imagej=True - ) - tf.imsave(str(file_name[0]) + "_component_masks.tiff", np.float32(self.decomp_map.T), imagej=True) - np.savetxt(str(file_name[0]) + "_deconv_spec.txt", self.decon_spectra) - np.savetxt(str(file_name[0]) + "_component_spec.txt", self.comp_spectra) - else: - pass - - def generateMultiColorView(self): - self.multichanneldict = {} - - for n, (colorName, image) in enumerate(zip(cmap_dict.keys(), self.comp_stack.transpose(0, 1, 2))): - low, high = np.min(image), np.max(image) - self.multichanneldict[f"Image {n + 1}"] = { - "ImageName": f"Image {n + 1}", - "ImageDir": ".", - "Image": image, - "Color": colorName, - "CmapLimits": (low, high), - "Opacity": 1.0, - } - self.muli_color_window = MultiChannelWindow(image_dict=self.multichanneldict) - self.muli_color_window.show() - - # add energy column - - -class ClusterViewer(QtWidgets.QMainWindow): - def __init__(self, decon_images, energy, X_cluster, decon_spectra): - super(ClusterViewer, self).__init__() - - # Load the UI Page - uic.loadUi(os.path.join(ui_path, "uis/ClusterView.ui"), self) - self.centralwidget.setStyleSheet(open(os.path.join(ui_path, "css/defaultStyle.css")).read()) - - self.decon_images = decon_images - self.energy = energy - self.X_cluster = X_cluster - self.decon_spectra = decon_spectra - (self.dim1, self.dim3, self.dim2) = self.decon_images.shape - self.hsb_cluster_number.setMaximum(self.dim1 - 1) - self.X_cluster = X_cluster - - self.image_view.setImage(self.decon_images, autoHistogramRange=True, autoLevels=True) - self.image_view.setPredefinedGradient("viridis") - self.image_view.ui.menuBtn.hide() - self.image_view.ui.roiBtn.hide() - - self.cluster_view.setImage(self.X_cluster, autoHistogramRange=True, autoLevels=True) - self.cluster_view.setPredefinedGradient("bipolar") - self.cluster_view.ui.histogram.hide() - self.cluster_view.ui.menuBtn.hide() - self.cluster_view.ui.roiBtn.hide() - - # connection - self.update_display() - self.hsb_cluster_number.valueChanged.connect(self.update_display) - self.actionSave.triggered.connect(self.save_clust_data) - self.pb_show_all_spec.clicked.connect(self.showAllSpec) - self.pb_showMultiColor.clicked.connect(self.generateMultiColorView) - - def update_display(self): - im_index = self.hsb_cluster_number.value() - self.component_view.setLabel("bottom", "Energy") - self.component_view.setLabel("left", "Intensity", "A.U.") - self.component_view.plot(self.energy, self.decon_spectra[:, im_index], clear=True) - # self.image_view.setCurrentIndex(im_index-1) - self.image_view.setImage(self.decon_images[im_index]) - self.label_comp_number.setText(f"{im_index + 1}/{self.dim1}") - - def save_clust_data(self): - file_name = QFileDialog().getSaveFileName(self, "", "", "data(*tiff *tif *txt *png )") - if file_name[0]: - - tf.imsave( - str(file_name[0]) + "_cluster.tiff", np.float32(self.decon_images.transpose(0, 2, 1)), imagej=True - ) - tf.imsave(str(file_name[0]) + "_cluster_map.tiff", np.float32(self.X_cluster.T), imagej=True) - np.savetxt(str(file_name[0]) + "_deconv_spec.txt", self.decon_spectra) - - else: - logger.error("Saving Cancelled") - self.statusbar.showMessage("Saving Cancelled") - pass - - def showAllSpec(self): - self.component_view.clear() - self.plt_colors = ["g", "b", "r", "c", "m", "y", "w"] * 10 - offsets = np.arange(0, 2, 0.2) - self.component_view.addLegend() - for ii in range(self.decon_spectra.shape[1]): - self.component_view.plot( - self.energy, - (self.decon_spectra[:, ii] / self.decon_spectra[:, ii].max()) + offsets[ii], - pen=self.plt_colors[ii], - name="cluster" + str(ii + 1), - ) - - def generateMultiColorView(self): - self.multichanneldict = {} - - for n, (colorName, image) in enumerate(zip(cmap_dict.keys(), self.decon_images.transpose(0, 1, 2))): - low, high = np.min(image), np.max(image) - self.multichanneldict[f"Image {n + 1}"] = { - "ImageName": f"Image {n + 1}", - "ImageDir": ".", - "Image": image, - "Color": colorName, - "CmapLimits": (low, high), - "Opacity": 1.0, - } - self.muli_color_window = MultiChannelWindow(image_dict=self.multichanneldict) - self.muli_color_window.show() - - -class XANESViewer(QtWidgets.QMainWindow): - def __init__(self, im_stack=None, e_list=None, refs=None, ref_names=None): - super(XANESViewer, self).__init__() - - uic.loadUi(os.path.join(ui_path, "uis/XANESViewer.ui"), self) - self.centralwidget.setStyleSheet(open(os.path.join(ui_path, "css/defaultStyle.css")).read()) - - self.im_stack = im_stack - self.e_list = e_list - self.refs = refs - self.ref_names = ref_names - self.selected = self.ref_names - self.fitResultDict = {} - self.fit_method = self.cb_xanes_fit_model.currentText() - self.alphaForLM = self.dsb_alphaForLM.value() - - self.decon_ims, self.rfactor, self.coeffs_arr = xanes_fitting( - self.im_stack, self.e_list, self.refs, method=self.fit_method, alphaForLM=self.alphaForLM - ) - - (self.dim1, self.dim2, self.dim3) = self.im_stack.shape - self.cn = int(self.dim2 // 2) - self.sz = np.max([int(self.dim2 * 0.15), int(self.dim3 * 0.15)]) - self.image_roi = pg.RectROI( - [int(self.dim3 // 2), int(self.dim2 // 2)], - [self.sz, self.sz], - pen="w", - maxBounds=QtCore.QRectF(0, 0, self.dim3, self.dim2), - ) - - self.image_roi.addTranslateHandle([0, 0], [2, 2]) - self.image_roi.addRotateHandle([0, 1], [2, 2]) - - # self.image_roi = pg.PolyLineROI([[0, 0], [0, self.sz], [self.sz, self.sz], [self.sz, 0]], - # pos=(int(self.dim2 // 2), int(self.dim3 // 2)), - # maxBounds=QtCore.QRect(0, 0, self.dim3, self.dim2), closed=True) - # self.image_roi.addTranslateHandle([self.sz // 2, self.sz // 2], [2, 2]) - - self.stack_center = int(self.dim1 // 2) - self.stack_width = int(self.dim1 * 0.05) - # self.image_view.setCurrentIndex(self.stack_center) - - self.image_view.addItem(self.image_roi) - self.xdata = self.e_list + self.sb_e_shift.value() - - self.scrollBar_setup() - self.display_image_data() - self.display_references() - self.update_spectrum() - - # connections - self.sb_e_shift.valueChanged.connect(self.update_spectrum) - self.pb_re_fit.clicked.connect(self.re_fit_xanes) - self.pb_edit_refs.clicked.connect(self.choose_refs) - self.image_roi.sigRegionChanged.connect(self.update_spectrum) - self.hsb_xanes_stk.valueChanged.connect(self.display_image_data) - self.hsb_chem_map.valueChanged.connect(self.display_image_data) - self.pb_showMultiColor.clicked.connect(self.generateMultiColorView) - self.pb_showCompSpec.clicked.connect(self.showComponentXANES) - - # menu - self.actionSave_Chem_Map.triggered.connect(self.save_chem_map) - self.actionSave_R_factor_Image.triggered.connect(self.save_rfactor_img) - self.actionSave_Live_Fit_Data.triggered.connect(self.pg_export_spec_fit) - self.actionExport_Fit_Stats.triggered.connect(self.exportFitResults) - self.actionExport_Ref_Plot.triggered.connect(self.pg_export_references) - - def scrollBar_setup(self): - self.hsb_xanes_stk.setValue(self.stack_center) - self.hsb_xanes_stk.setMaximum(self.dim1 - 1) - self.hsb_chem_map.setValue(0) - self.hsb_chem_map.setMaximum(self.decon_ims.shape[-1] - 1) - - def display_image_data(self): - - self.image_view.setImage(self.im_stack[self.hsb_xanes_stk.value()]) - self.image_view.ui.menuBtn.hide() - self.image_view.ui.roiBtn.hide() - self.image_view.setPredefinedGradient("viridis") - - self.image_view_maps.setImage(self.decon_ims.transpose(2, 0, 1)[self.hsb_chem_map.value()]) - self.image_view_maps.setPredefinedGradient("bipolar") - self.image_view_maps.ui.menuBtn.hide() - self.image_view_maps.ui.roiBtn.hide() - - def display_references(self): - - self.inter_ref = interploate_E(self.refs, self.xdata) - self.plt_colors = ["c", "m", "y", "w"] * 10 - self.spectrum_view_refs.addLegend() - for ii in range(self.inter_ref.shape[0]): - if len(self.selected) != 0: - self.spectrum_view_refs.plot( - self.xdata, - self.inter_ref[ii], - pen=pg.mkPen(self.plt_colors[ii], width=2), - name=self.selected[1:][ii], - ) - else: - self.spectrum_view_refs.plot( - self.xdata, - self.inter_ref[ii], - pen=pg.mkPen(self.plt_colors[ii], width=2), - name="ref" + str(ii + 1), - ) - - def choose_refs(self): - "Interactively exclude some standards from the reference file" - self.ref_edit_window = RefChooser( - self.ref_names, - self.im_stack, - self.e_list, - self.refs, - self.sb_e_shift.value(), - self.cb_xanes_fit_model.currentText(), - ) - self.ref_edit_window.show() - # self.rf_plot = pg.plot(title="RFactor Tracker") - - # connections - self.ref_edit_window.choosenRefsSignal.connect(self.update_refs) - self.ref_edit_window.fitResultsSignal.connect(self.plotFitResults) - - def update_refs(self, list_): - self.selected = list_ # list_ is the signal from ref chooser - self.update_spectrum() - self.re_fit_xanes() - - def update_spectrum(self): - - self.roi_img = self.image_roi.getArrayRegion(self.im_stack, self.image_view.imageItem, axes=(1, 2)) - sizex, sizey = self.roi_img.shape[1], self.roi_img.shape[2] - posx, posy = self.image_roi.pos() - self.roi_info.setText(f"ROI_Pos: {int(posx)},{int(posy)} ROI_Size: {sizex},{sizey}") - - self.xdata1 = self.e_list + self.sb_e_shift.value() - self.ydata1 = get_sum_spectra(self.roi_img) - self.fit_method = self.cb_xanes_fit_model.currentText() - - if len(self.selected) != 0: - - self.inter_ref = interploate_E(self.refs[self.selected], self.xdata1) - stats, coeffs = xanes_fitting_1D( - self.ydata1, - self.xdata1, - self.refs[self.selected], - method=self.fit_method, - alphaForLM=self.alphaForLM, - ) - - else: - self.inter_ref = interploate_E(self.refs, self.xdata1) - stats, coeffs = xanes_fitting_1D( - self.ydata1, self.xdata1, self.refs, method=self.fit_method, alphaForLM=self.alphaForLM - ) - - self.fit_ = np.dot(coeffs, self.inter_ref) - pen = pg.mkPen("g", width=1.5) - pen2 = pg.mkPen("r", width=1.5) - # pen3 = pg.mkPen("y", width=1.5) - self.spectrum_view.addLegend() - self.spectrum_view.setLabel("bottom", "Energy") - self.spectrum_view.setLabel("left", "Intensity", "A.U.") - self.spectrum_view.plot(self.xdata1, self.ydata1, pen=pen, name="Data", clear=True) - self.spectrum_view.plot(self.xdata1, self.fit_, name="Fit", pen=pen2) - - for n, (coff, ref, plt_clr) in enumerate(zip(coeffs, self.inter_ref, self.plt_colors)): - - if len(self.selected) != 0: - - self.spectrum_view.plot(self.xdata1, np.dot(coff, ref), name=self.selected[1:][n], pen=plt_clr) - else: - self.spectrum_view.plot(self.xdata1, np.dot(coff, ref), name="ref" + str(n + 1), pen=plt_clr) - # set the rfactor value to the line edit slot - self.results = ( - f"Coefficients: {coeffs} \n" - f"R-Factor: {stats['R_Factor']}, R-Square: {stats['R_Square']},\n " - f"Chi-Square: {stats['Chi_Square']}, " - f"Reduced Chi-Square: {stats['Reduced Chi_Square']}" - ) - - self.fit_results.setText(self.results) - - def re_fit_xanes(self): - if len(self.selected) != 0: - self.decon_ims, self.rfactor, self.coeffs_arr = xanes_fitting( - self.im_stack, - self.e_list + self.sb_e_shift.value(), - self.refs[self.selected], - method=self.cb_xanes_fit_model.currentText(), - alphaForLM=self.alphaForLM, - ) - else: - # if non athena file with no header is loaded no ref file cannot be edited - self.decon_ims, self.rfactor, self.coeffs_arr = xanes_fitting( - self.im_stack, - self.e_list + self.sb_e_shift.value(), - self.refs, - method=self.cb_xanes_fit_model.currentText(), - alphaForLM=self.alphaForLM, - ) - - # rfactor is a list of all spectra so take the mean - self.rfactor_mean = np.mean(self.rfactor) - self.image_view_maps.setImage(self.decon_ims.transpose(2, 0, 1)) - self.scrollBar_setup() - - def plotFitResults(self, decon_ims, rfactor_mean, coeff_array): - # upadte the chem maps and scrollbar params - self.image_view_maps.setImage(decon_ims.transpose(2, 0, 1)) - # self.hsb_chem_map.setValue(0) - # self.hsb_chem_map.setMaximum(decon_ims.shape[-1]-1) - - # set the rfactor value to the line edit slot - self.le_r_sq.setText(f"{rfactor_mean :.4f}") - - def showComponentXANES(self): - compNum = self.hsb_chem_map.value() - currentComp = self.decon_ims.transpose(2, 0, 1)[compNum] - currentCompMask = currentComp > 0 - yData = applyMaskGetMeanSpectrum(self.im_stack, currentCompMask) - xanes_comp_plot = pg.plot( - self.e_list + self.sb_e_shift.value(), - yData, - title=f"Component_{compNum}", - pen=pg.mkPen("y", width=2, style=QtCore.Qt.DotLine), - symbol="o", - ) - xanes_comp_plot.setLabel("bottom", "Energy (keV)") - xanes_comp_plot.setLabel("left", "Intensity") - - def generateMultiColorView(self): - self.multichanneldict = {} - - for n, (colorName, image) in enumerate(zip(cmap_dict.keys(), self.decon_ims.transpose((2, 0, 1)))): - low, high = np.min(image), np.max(image) - self.multichanneldict[f"Image {n + 1}"] = { - "ImageName": f"Image {n + 1}", - "ImageDir": ".", - "Image": image, - "Color": colorName, - "CmapLimits": (low, high), - "Opacity": 1.0, - } - self.muli_color_window = MultiChannelWindow(image_dict=self.multichanneldict) - self.muli_color_window.show() - - def save_chem_map(self): - file_name = QFileDialog().getSaveFileName(self, "save image", "chemical_map.tiff", "image data (*tiff)") - if file_name[0]: - tf.imsave(str(file_name[0]), np.float32(self.decon_ims.transpose(2, 0, 1)), imagej=True) - else: - logger.error("No file to save") - pass - - def save_rfactor_img(self): - file_name = QFileDialog().getSaveFileName(self, "save image", "r-factor_map.tiff", "image data (*tiff)") - if file_name[0]: - tf.imsave(str(file_name[0]), np.float32(self.rfactor), imagej=True) - else: - logger.error("No file to save") - pass - - def save_spec_fit(self): - try: - to_save = np.column_stack([self.xdata1, self.ydata1, self.fit_]) - file_name = QFileDialog().getSaveFileName(self, "save spectrum", "", "spectrum and fit (*txt)") - if file_name[0]: - np.savetxt(str(file_name[0]) + ".txt", to_save) - else: - pass - except Exception: - logger.error("No file to save") - pass - - def pg_export_spec_fit(self): - - exporter = pg.exporters.CSVExporter(self.spectrum_view.plotItem) - exporter.parameters()["columnMode"] = "(x,y,y,y) for all plots" - file_name = QFileDialog().getSaveFileName(self, "save spectrum", "", "spectrum and fit (*csv)") - if file_name[0]: - exporter.export(str(file_name[0]) + ".csv") - else: - pass - - def pg_export_references(self): - - exporter = pg.exporters.CSVExporter(self.spectrum_view_refs.plotItem) - exporter.parameters()["columnMode"] = "(x,y,y,y) for all plots" - file_name = QFileDialog().getSaveFileName( - self, "save references", "xanes_references.csv", "column data (*csv)" - ) - if file_name[0]: - exporter.export(str(file_name[0])) - else: - pass - - def exportFitResults(self): - file_name = QFileDialog().getSaveFileName(self, "save txt", "xanes_1D_fit_results.txt", "txt data (*txt)") - if file_name[0]: - with open(file_name[0], "w") as file: - file.write(self.results) - else: - pass - - -class RefChooser(QtWidgets.QMainWindow): - choosenRefsSignal: pyqtSignal = QtCore.pyqtSignal(list) - fitResultsSignal: pyqtSignal = QtCore.pyqtSignal(np.ndarray, float, np.ndarray) - - def __init__(self, ref_names, im_stack, e_list, refs, e_shift, fit_model): - super(RefChooser, self).__init__() - uic.loadUi(os.path.join(ui_path, "uis/RefChooser.ui"), self) - self.centralwidget.setStyleSheet(open(os.path.join(ui_path, "css/defaultStyle.css")).read()) - self.ref_names = ref_names - self.refs = refs - self.im_stack = im_stack - self.e_list = e_list - self.e_shift = e_shift - self.fit_model = fit_model - - self.all_boxes = [] - self.rFactorList = [] - - self.displayCombinations() - - # selection become more apparent than default with red-ish color - self.tableWidget.setStyleSheet("background-color: white; selection-background-color: rgb(200,0,0);") - - # add a line to the plot to walk through the table. Note that the table is not sorted - self.selectionLine = pg.InfiniteLine( - pos=1, angle=90, pen=pg.mkPen("m", width=2.5), movable=True, bounds=None, label="Move Me!" - ) - self.stat_view.setLabel("bottom", "Fit ID") - self.stat_view.setLabel("left", "Reduced Chi^2") - - for n, i in enumerate(self.ref_names): - self.cb_i = QtWidgets.QCheckBox(self.ref_box_frame) - if n == 0: - self.cb_i.setChecked(True) - self.cb_i.setEnabled(False) - self.cb_i.setObjectName(i) - self.cb_i.setText(i) - self.gridLayout_2.addWidget(self.cb_i, n, 0, 1, 1) - self.cb_i.toggled.connect(self.enableApply) - self.all_boxes.append(self.cb_i) - - # connections - self.pb_apply.clicked.connect(self.clickedWhichAre) - self.pb_combo.clicked.connect(self.tryAllCombo) - self.actionExport_Results_csv.triggered.connect(self.exportFitResults) - self.selectionLine.sigPositionChanged.connect(self.updateFitWithLine) - self.tableWidget.itemSelectionChanged.connect(self.updateWithTableSelection) - # self.stat_view.scene().sigMouseClicked.connect(self.moveSelectionLine) - self.stat_view.mouseDoubleClickEvent = self.moveSelectionLine - self.sb_max_combo.valueChanged.connect(self.displayCombinations) - # self.pb_sort_with_r.clicked.connect(lambda: self.tableWidget.sortItems(3, QtCore.Qt.AscendingOrder)) - self.pb_sort_with_r.clicked.connect(self.sortTable) - self.cb_sorter.currentTextChanged.connect(self.sortTable) - - # def clickedWhich(self): - # button_name = self.sender() - - def populateChecked(self): - self.onlyCheckedBoxes = [] - for names in self.all_boxes: - if names.isChecked(): - self.onlyCheckedBoxes.append(names.objectName()) - - QtCore.pyqtSlot() - - def clickedWhichAre(self): - self.populateChecked() - self.choosenRefsSignal.emit(self.onlyCheckedBoxes) - - def generateRefList(self, ref_list, maxCombo, minCombo=1): - - """ - Creates a list of reference combinations for xanes fitting - - Paramaters; - - ref_list (list): list of ref names from the header - maxCombo (int): maximum number of ref lists in combination - minCombo (int): min number of ref lists in combination - - returns; - - 1. int: length of total number of combinations - 2. list: all the combinations - - """ - - if not maxCombo > len(ref_list): - - iter_list = [] - while minCombo < maxCombo + 1: - iter_list += list(combinations(ref_list, minCombo)) - minCombo += 1 - return len(iter_list), iter_list - - else: - raise ValueError(" Maximum numbinations cannot be larger than number of list items") - - def displayCombinations(self): - niter, self.iter_list = self.generateRefList(self.ref_names[1:], self.sb_max_combo.value()) - self.label_nComb.setText(str(niter) + " Combinations") - - @QtCore.pyqtSlot() - def tryAllCombo(self): - # empty list to to keep track and plot of reduced chi2 of all the fits - self.rfactor_list = [] - - # create dataframe for the table - self.df = pd.DataFrame( - columns=["Fit Number", "References", "Coefficients", "R-Factor", "R^2", "chi^2", "red-chi^2", "Score"] - ) - - # df columns is the header for the table widget - self.tableWidget.setHorizontalHeaderLabels(self.df.columns) - # self.iter_list = list(combinations(self.ref_names[1:],self.sb_max_combo.value())) - - niter, self.iter_list = self.generateRefList(self.ref_names[1:], self.sb_max_combo.value()) - tot_combo = len(self.iter_list) - for n, refs in enumerate(self.iter_list): - self.statusbar.showMessage(f"{n + 1}/{tot_combo}") - selectedRefs = list((str(self.ref_names[0]),) + refs) - self.fit_combo_progress.setValue((n + 1) * 100 / tot_combo) - self.stat, self.coeffs_arr = xanes_fitting_Binned( - self.im_stack, self.e_list + self.e_shift, self.refs[selectedRefs], method=self.fit_model - ) - - self.rfactor_list.append(self.stat["Reduced Chi_Square"]) - self.stat_view.plot( - x=np.arange(n + 1), - y=self.rfactor_list, - clear=True, - title="Reduced Chi^2", - pen=pg.mkPen("y", width=2, style=QtCore.Qt.DotLine), - symbol="o", - ) - - # arbitary number to rank the best fit - fit_score = (self.stat["R_Square"] + np.sum(self.coeffs_arr)) / ( - self.stat["R_Factor"] + self.stat["Reduced Chi_Square"] - ) - - resultsDict = { - "Fit Number": n, - "References": str(selectedRefs[1:]), - "Coefficients": str(np.around(self.coeffs_arr, 4)), - "Sum of Coefficients": str(np.around(np.sum(self.coeffs_arr), 4)), - "R-Factor": self.stat["R_Factor"], - "R^2": self.stat["R_Square"], - "chi^2": self.stat["Chi_Square"], - "red-chi^2": self.stat["Reduced Chi_Square"], - "Score": np.around(fit_score, 4), - } - - self.df = pd.concat([self.df, pd.DataFrame([resultsDict])], ignore_index=True) - - self.dataFrametoQTable(self.df) - QtTest.QTest.qWait(0.1) # hepls with real time plotting - - self.stat_view.addItem(self.selectionLine) - - def dataFrametoQTable(self, df_: pd.DataFrame): - nRows = len(df_.index) - nColumns = len(df_.columns) - self.tableWidget.setRowCount(nRows) - self.tableWidget.setColumnCount(nColumns) - self.tableWidget.setHorizontalHeaderLabels(df_.columns) - - for i in range(nRows): - for j in range(nColumns): - cell = QtWidgets.QTableWidgetItem(str(df_.values[i][j])) - self.tableWidget.setItem(i, j, cell) - - # set the property of the table view. Size policy to make the contents justified - self.tableWidget.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents) - self.tableWidget.resizeColumnsToContents() - - def exportFitResults(self): - file_name = QFileDialog().getSaveFileName(self, "save csv", "xanes_fit_results_log.csv", "txt data (*csv)") - if file_name[0]: - with open(str(file_name[0]), "w") as fp: - self.df.to_csv(fp) - else: - pass - - def selectTableAndCheckBox(self, x): - nSelection = int(round(x)) - self.tableWidget.selectRow(nSelection) - fit_num = int(self.tableWidget.item(nSelection, 0).text()) - refs_selected = self.iter_list[fit_num] - - # reset all the checkboxes to uncheck state, except the energy - for checkstate in self.findChildren(QtWidgets.QCheckBox): - if checkstate.isEnabled(): - checkstate.setChecked(False) - - for cb_names in refs_selected: - checkbox = self.findChild(QtWidgets.QCheckBox, name=cb_names) - checkbox.setChecked(True) - - def updateFitWithLine(self): - pos_x, pos_y = self.selectionLine.pos() - x = self.df.index[self.df[str("Fit Number")] == np.round(pos_x)][0] - self.selectTableAndCheckBox(x) - - def updateWithTableSelection(self): - x = self.tableWidget.currentRow() - self.selectTableAndCheckBox(x) - - def moveSelectionLine(self, event): - if event.button() == QtCore.Qt.LeftButton: - Pos = self.stat_view.plotItem.vb.mapSceneToView(event.pos()) - self.selectionLine.setPos(Pos.x()) - - def sortTable(self): - sorter_dict = { - "R-Factor": "R-Factor", - "R-Square": "R^2", - "Chi-Square": "chi^2", - "Reduced Chi-Square": "red-chi^2", - "Fit Number": "Fit Number", - } - sorter = sorter_dict[self.cb_sorter.currentText()] - self.df = self.df.sort_values(sorter, ignore_index=True) - self.dataFrametoQTable(self.df) - - def enableApply(self): - - """ """ - self.populateChecked() - if len(self.onlyCheckedBoxes) > 1: - self.pb_apply.setEnabled(True) - else: - self.pb_apply.setEnabled(False) - - -class ScatterPlot(QtWidgets.QMainWindow): - def __init__(self, img1, img2, nameTuple): - super(ScatterPlot, self).__init__() - - uic.loadUi(os.path.join(ui_path, "uis/ScatterView.ui"), self) - self.centralwidget.setStyleSheet(open(os.path.join(ui_path, "css/defaultStyle.css")).read()) - self.clearPgPlot() - self.w1 = self.scatterViewer.addPlot() - self.img1 = img1 - self.img2 = img2 - self.nameTuple = nameTuple - x, y = np.shape(self.img1) - self.s1 = pg.ScatterPlotItem(size=2, pen=pg.mkPen(None), brush=pg.mkBrush(255, 255, 0, 255)) - # print(self.s1) - - # create three polyline ROIs for masking - Xsize = self.img1.max() / 6 - Ysize = self.img2.max() / 6 - - self.scatter_mask = pg.PolyLineROI( - [[0, 0], [0, Ysize], [Xsize / 2, Ysize * 1.5], [Xsize, Ysize], [Xsize, 0]], - pos=None, - pen=pg.mkPen("r", width=2), - hoverPen=pg.mkPen("w", width=2), - closed=True, - removable=True, - ) - - self.scatter_mask2 = pg.PolyLineROI( - [ - [Xsize * 1.2, 0], - [Xsize * 1.2, Ysize * 2], - [Xsize * 2, Ysize * 2], - [Xsize * 3, Ysize], - [Xsize * 2, 0], - ], - pos=None, - pen=pg.mkPen("g", width=2), - hoverPen=pg.mkPen("w", width=2), - closed=True, - removable=True, - ) - self.scatter_mask3 = pg.PolyLineROI( - [ - [Xsize * 2.5, 0], - [Xsize * 2.5, Ysize], - [Xsize * 4, Ysize], - [Xsize * 4, 0], - [Xsize * 3.7, Ysize * -0.5], - ], - pos=None, - pen=pg.mkPen("c", width=2), - hoverPen=pg.mkPen("w", width=2), - closed=True, - removable=True, - ) - - self.fitScatter = self.fitScatter2 = self.fitScatter3 = None - - self.rois = { - "ROI 1": (self.scatter_mask, self.rb_roi1.isChecked(), self.fitScatter), - "ROI 2": (self.scatter_mask2, self.rb_roi2.isChecked(), self.fitScatter2), - "ROI 3": (self.scatter_mask3, self.rb_roi3.isChecked(), self.fitScatter3), - } - - self.windowNames = {"ROI 1": self.fitScatter, "ROI 2": self.fitScatter2, "ROI 3": self.fitScatter3} - - self.s1.setData(self.img1.flatten(), self.img2.flatten()) - self.w1.setLabel("bottom", self.nameTuple[0], "counts") - self.label_img1.setText(self.nameTuple[0]) - self.w1.setLabel("left", self.nameTuple[1], "counts") - self.label_img2.setText(self.nameTuple[1]) - self.w1.addItem(self.s1) - - self.image_view.setImage(self.img1) - self.image_view.ui.menuBtn.hide() - self.image_view.ui.roiBtn.hide() - self.image_view.setPredefinedGradient("thermal") - - self.image_view2.setImage(self.img2) - self.image_view2.ui.menuBtn.hide() - self.image_view2.ui.roiBtn.hide() - self.image_view2.setPredefinedGradient("thermal") - - # connections - self.actionSave_Plot.triggered.connect(self.pg_export_correlation) - self.actionSave_Images.triggered.connect(self.tiff_export_images) - # self.pb_define_mask.clicked.connect(lambda:self.createMask(self.scatter_mask)) - self.pb_define_mask.clicked.connect(self.addMultipleROIs) - # self.pb_apply_mask.clicked.connect(lambda:self.getMaskRegion(self.scatter_mask)) - self.pb_apply_mask.clicked.connect(self.applyMultipleROIs) - self.pb_clear_mask.clicked.connect(self.clearMultipleROIs) - self.pb_compositeScatter.clicked.connect(self.createCompositeScatter) - [rbs.clicked.connect(self.updateROIDict) for rbs in [self.rb_roi1, self.rb_roi2, self.rb_roi3]] - - def pg_export_correlation(self): - - exporter = pg.exporters.CSVExporter(self.w1) - exporter.parameters()["columnMode"] = "(x,y,y,y) for all plots" - file_name = QFileDialog().getSaveFileName(self, "save correlation", "", "spectrum and fit (*csv)") - if file_name[0]: - exporter.export(str(file_name[0]) + ".csv") - self.statusbar.showMessage(f"Data saved to {str(file_name[0])}") - else: - pass - - def tiff_export_images(self): - file_name = QFileDialog().getSaveFileName(self, "save images", "", "spectrum and fit (*tiff)") - if file_name[0]: - tf.imsave(str(file_name[0]) + ".tiff", np.dstack([self.img1, self.img2]).T) - self.statusbar.showMessage(f"Images saved to {str(file_name[0])}") - else: - pass - - def createMask(self, ROIName): - - try: - self.w1.removeItem(ROIName) - except Exception: - pass - self.w1.addItem(ROIName) - - def clearMask(self, ROIName): - self.w1.removeItem(ROIName) - - def clearPgPlot(self): - try: - self.masked_img.close() - except Exception: - pass - - def getMaskRegion(self, ROIName, generateSeperateWindows=True): - - """filter scatterplot points using polylineROI region""" - - # Ref : https://stackoverflow.com/questions/57719303/how-to-map-mouse-position-on-a-scatterplot - - # get the roi region:QPaintPathObject - roiShape = self.rois[ROIName][0].mapToItem(self.s1, self.rois[ROIName][0].shape()) - - # get data in the scatter plot - scatterData = np.array(self.s1.getData()) - - # generate a binary mask for points inside or outside the roishape - selected = [roiShape.contains(QtCore.QPointF(pt[0], pt[1])) for pt in scatterData.T] - - # reshape the mask to image dimensions - self.mask2D = np.reshape(selected, (self.img1.shape)) - - # get masked image1 - self.maskedImage = self.mask2D * self.img1 - - # get rid of the (0,0) values in the masked array - self.xData, self.yData = np.compress(selected, scatterData[0]), np.compress(selected, scatterData[1]) - - # linear regeression of the filtered X,Y data - result = linregress(self.xData, self.yData) - - # Pearson's correlation of the filtered X,Y data - pr, pp = stats.pearsonr(self.xData, self.yData) - - # apply the solved equation to xData to generate the fit line - self.yyData = result.intercept + result.slope * self.xData - - # Prepare strings for fit results and stats - self.fitLineEqn = ( - f" y = x*{result.slope :.3e} + {result.intercept :.3e}, " - "R^2 = {result.rvalue**2 :.3f}, r = {pr :.3f}\n" - ) - FitStats1 = f" Slope Error = {result.stderr :.3e}, Intercept Error = {result.intercept_stderr :.3e}\n" - FitStats2 = f" Pearson’s correlation coefficient = {pr :.3f}" - refs = "\n\n ***References****\n\n scipy.stats.linregress, scipy.stats.pearsonr " - fitStats = ( - f"\n ***{ROIName} Fit Results***\n\n" + " Equation: " + self.fitLineEqn + FitStats1 + FitStats2 + refs - ) - - # generate new window to plot the results - - if generateSeperateWindows: - self.windowNames[ROIName] = MaskedScatterPlotFit( - [self.xData, self.yData], - [self.xData, self.yyData], - self.mask2D, - self.maskedImage, - fitStats, - self.fitLineEqn, - self.nameTuple, - ) - self.windowNames[ROIName].show() - - """ - from scipy.linalg import lstsq - M = xData[:, np.newaxis]**[0, 1] #use >1 for polynomial fits - p, res, rnk, s = lstsq(M, yData) - yyData = p[0] + p[1]*xData - """ - - def updateROIDict(self): - self.rois = { - "ROI 1": (self.scatter_mask, self.rb_roi1.isChecked()), - "ROI 2": (self.scatter_mask2, self.rb_roi2.isChecked()), - "ROI 3": (self.scatter_mask3, self.rb_roi3.isChecked()), - } - - def applyMultipleROIs(self): - with pg.BusyCursor(): - self.updateROIDict() - for key in self.rois.keys(): - if self.rois[key][1]: - self.getMaskRegion(key) - else: - pass - - def addMultipleROIs(self): - self.updateROIDict() - for key in self.rois.keys(): - if self.rois[key][1]: - self.createMask(self.rois[key][0]) - else: - self.clearMask(self.rois[key][0]) - - def clearMultipleROIs(self): - self.updateROIDict() - for key in self.rois.keys(): - if not self.rois[key][1]: - self.clearMask(self.rois[key][0]) - else: - pass - - def createCompositeScatter(self): - - points = [] - fitLine = [] - masks = [] - roiFitEqn = {} - - self.updateROIDict() - for n, key in enumerate(self.rois.keys()): - if self.rois[key][1]: - self.getMaskRegion(key, generateSeperateWindows=False) - points.append(np.column_stack([self.xData, self.yData])) - fitLine.append(np.column_stack([self.xData, self.yyData])) - masks.append(self.mask2D) - roiFitEqn[key] = self.fitLineEqn - else: - pass - - logger.info(f" fitline shape: {np.shape(fitLine)}") - logger.info(f" points shape: {np.shape(points)}") - logger.info(f" maks shape: {np.shape(masks)}") - self.compositeScatterWindow = CompositeScatterPlot( - np.array(points), np.array(fitLine), np.array(masks), roiFitEqn, self.nameTuple - ) - self.compositeScatterWindow.show() - - def _createCompositeScatter(self): - self.scatterColors = ["w", "c", "y", "k", "m"] - points = [] - fitLine = [] - - self.updateROIDict() - for n, key in enumerate(self.rois.keys()): - if self.rois[key][1]: - self.getMaskRegion(key, generateSeperateWindows=False) - - for x, y, yy in zip(self.xData, self.yData, self.yyData): - - points.append( - { - "pos": (x, y), - "data": "id", - "size": 3, - "pen": pg.mkPen(None), - "brush": self.scatterColors[n], - } - ) - fitLine.extend(np.column_stack((self.xData, self.yyData))) - else: - pass - - logger.info(f" fitline shape: {np.shape(fitLine)}") - self.compositeScatterWindow = CompositeScatterPlot(points, np.array(fitLine)) - self.compositeScatterWindow.show() - - def getROIParams(self): - print(np.array(self.scatter_mask.getSceneHandlePositions())) - - -class MaskedScatterPlotFit(QtWidgets.QMainWindow): - def __init__(self, scatterData, fitData, mask, maskedImage, fitString, fitEquation, nameTuple): - super(MaskedScatterPlotFit, self).__init__() - - uic.loadUi(os.path.join(ui_path, "uis/maskedScatterPlotFit.ui"), self) - self.centralwidget.setStyleSheet(open(os.path.join(ui_path, "css/defaultStyle.css")).read()) - self.scatterData = scatterData - self.fitData = fitData - self.mask = mask - self.maskedImage = maskedImage - self.fitString = fitString - self.fitEquation = fitEquation - self.nameTuple = nameTuple - - # set the graphicslayoutwidget in the ui as canvas - self.canvas = self.scatterViewer.addPlot() - self.canvas.addLegend() - self.canvas.setLabel("bottom", self.nameTuple[0], "counts") - self.canvas.setLabel("left", self.nameTuple[1], "counts") - self.gb_maskedImage1.setTitle(f" Masked {self.nameTuple[0]}") - - # generate a scatter plot item - self.scattered = pg.ScatterPlotItem(size=3.5, pen=pg.mkPen(None), brush=pg.mkBrush(5, 214, 255, 200)) - - # set scatter plot data - self.scattered.setData(scatterData[0], scatterData[1], name="Data") - - # set z value negative to show scatter data behind the fit line - self.scattered.setZValue(-10) - - # add scatter plot to the canvas - self.canvas.addItem(self.scattered) - - # generate plotitem for fit line - self.fitLinePlot = pg.PlotDataItem(pen=pg.mkPen(pg.mkColor(220, 20, 60), width=3.3)) - - # set line plot data - self.fitLinePlot.setData(fitData[0], fitData[1], name="Linear Fit") - - # add line plot to the canvas - self.canvas.addItem(self.fitLinePlot) - - # display Mask - self.imageView_mask.setImage(self.mask) - self.imageView_mask.ui.menuBtn.hide() - self.imageView_mask.ui.roiBtn.hide() - self.imageView_mask.setPredefinedGradient("plasma") - - # display masked Image - self.imageView_maskedImage.setImage(self.maskedImage) - self.imageView_maskedImage.ui.menuBtn.hide() - self.imageView_maskedImage.ui.roiBtn.hide() - self.imageView_maskedImage.setPredefinedGradient("viridis") - - # display Fit stats - self.text_fit_results.setPlainText(fitString) - self.canvas.setTitle(self.fitEquation, color="r") - - # connections - self.pb_copy_results.clicked.connect(self.copyFitResults) - self.pb_save_results.clicked.connect(self.saveFitResults) - self.actionSave_Plot.triggered.connect(self.pg_export_correlation) - self.actionSaveMask.triggered.connect(self.saveMask) - self.actionSaveMaskedImage.triggered.connect(self.saveImage) - - def saveFitResults(self): - S__File = QFileDialog.getSaveFileName(self, "save txt", "correlationPlotFit.txt", "txt data (*txt)") - - Text = self.text_fit_results.toPlainText() - if S__File[0]: - with open(S__File[0], "w") as file: - file.write(Text) - - def copyFitResults(self): - self.text_fit_results.selectAll() - self.text_fit_results.copy() - self.statusbar.showMessage("text copied to clipboard") - - def pg_export_correlation(self): - - exporter = pg.exporters.CSVExporter(self.canvas) - exporter.parameters()["columnMode"] = "(x,y,y,y) for all plots" - file_name = QFileDialog().getSaveFileName( - self, "save correlation", "scatterData.csv", "spectrum and fit (*csv)" - ) - if file_name[0]: - exporter.export(str(file_name[0])) - self.statusbar.showMessage(f"Data saved to {str(file_name[0])}") - else: - pass - - def saveImage(self): - - file_name = QFileDialog().getSaveFileName(self, "Save image data", "image.tiff", "image file(*tiff *tif )") - if file_name[0]: - tf.imsave(str(file_name[0]), self.maskedImage) - self.statusbar.showMessage(f"Data saved to {str(file_name[0])}") - else: - self.statusbar.showMessage("Saving cancelled") - pass - - def saveMask(self): - - file_name = QFileDialog().getSaveFileName(self, "Save image data", "mask.tiff", "image file(*tiff *tif )") - if file_name[0]: - tf.imsave(str(file_name[0]), self.mask) - self.statusbar.showMessage(f"Data saved to {str(file_name[0])}") - else: - self.statusbar.showMessage("Saving cancelled") - pass - - -class ComponentScatterPlot(QtWidgets.QMainWindow): - def __init__(self, decomp_stack, specs): - super(ComponentScatterPlot, self).__init__() - - uic.loadUi(os.path.join(ui_path, "uis/ComponentScatterPlot.ui"), self) - self.centralwidget.setStyleSheet(open(os.path.join(ui_path, "css/defaultStyle.css")).read()) - self.w1 = self.scatterViewer.addPlot() - self.decomp_stack = decomp_stack - self.specs = specs - (self.dim1, self.dim3, self.dim2) = self.decomp_stack.shape - # fill the combonbox depending in the number of components for scatter plot - for n, combs in enumerate(combinations(np.arange(self.dim1), 2)): - self.cb_scatter_comp.addItem(str(combs)) - self.cb_scatter_comp.setItemData(n, combs) - - self.s1 = pg.ScatterPlotItem(size=3, pen=pg.mkPen(None), brush=pg.mkBrush(255, 255, 0, 120)) - - self.setImageAndScatterPlot() - # connections - self.actionSave_Plot.triggered.connect(self.pg_export_correlation) - self.actionSave_Images.triggered.connect(self.tiff_export_images) - self.pb_updateComponents.clicked.connect(self.setImageAndScatterPlot) - self.pb_define_mask.clicked.connect(self.createMask) - self.pb_apply_mask.clicked.connect(self.getMaskRegion) - self.pb_reset_mask.clicked.connect(self.resetMask) - self.pb_addALine.clicked.connect(lambda: self.createMask(Line=True)) - - def setImageAndScatterPlot(self): - - try: - self.s1.clear() - except Exception: - pass - - comp_tuple = self.cb_scatter_comp.currentData() - self.img1, self.img2 = self.decomp_stack[comp_tuple[0]], self.decomp_stack[comp_tuple[-1]] - self.image_view.setImage(self.decomp_stack[comp_tuple[0]]) - self.image_view.ui.menuBtn.hide() - self.image_view.ui.roiBtn.hide() - self.image_view.setPredefinedGradient("bipolar") - - self.image_view2.setImage(self.decomp_stack[comp_tuple[-1]]) - self.image_view2.ui.menuBtn.hide() - self.image_view2.ui.roiBtn.hide() - self.image_view2.setPredefinedGradient("bipolar") - - points = [] - for i, j in zip(self.img1.flatten(), self.img2.flatten()): - - points.append( - { - "pos": (i, j), - "data": "id", - "size": 5, - "pen": pg.mkPen(None), - "brush": pg.mkBrush(255, 255, 0, 160), - } - ) - - self.s1.addPoints(points) - self.w1.addItem(self.s1) - # self.s1.setData(self.specs[:, comp_tuple[0]], self.specs[:, comp_tuple[-1]]) - self.w1.setLabel("bottom", f"PC{comp_tuple[0]+1}") - self.w1.setLabel("left", f"PC{comp_tuple[-1]+1}") - self.label_im1.setText(f"PC{comp_tuple[0]+1}") - self.label_im2.setText(f"PC{comp_tuple[-1]+1}") - - def createMask(self, Line=False): - - self.size = self.img1.max() / 10 - self.pos = int(self.img1.mean()) - - if Line: - self.lineROI = pg.LineSegmentROI( - [0, 1], - pos=(self.pos, self.pos), - pen=pg.mkPen("r", width=4), - hoverPen=pg.mkPen("g", width=4), - removable=True, - ) - self.w1.addItem(self.lineROI) - - else: - - self.scatter_mask = pg.PolyLineROI( - [[0, 0], [0, self.size], [self.size, self.size], [self.size, 0]], - pos=(self.pos, self.pos), - pen=pg.mkPen("r", width=4), - hoverPen=pg.mkPen("g", width=4), - closed=True, - removable=True, - ) - - self.w1.addItem(self.scatter_mask) - - def resetMask(self): - self.clearMask() - self.createMask() - - def clearMask(self): - try: - self.w1.removeItem(self.scatter_mask) - except AttributeError: - pass - - def clearPgPlot(self): - try: - self.masked_img.close() - except Exception: - pass - - def getMaskRegion(self): - - # Ref : https://stackoverflow.com/questions/57719303/how-to-map-mouse-position-on-a-scatterplot - - roiShape = self.scatter_mask.mapToItem(self.s1, self.scatter_mask.shape()) - self._points = list() - logger.info("Building Scatter Plot Window; Please wait..") - for i in range(len(self.img1.flatten())): - self._points.append(QtCore.QPointF(self.img1.flatten()[i], self.img2.flatten()[i])) - - selected = [roiShape.contains(pt) for pt in self._points] - img_selected = np.reshape(selected, (self.img1.shape)) - - self.masked_img = singleStackViewer(img_selected * self.img1, gradient="bipolar") - self.masked_img.show() - - def pg_export_correlation(self): - - exporter = pg.exporters.CSVExporter(self.w1) - exporter.parameters()["columnMode"] = "(x,y,y,y) for all plots" - file_name = QFileDialog().getSaveFileName(self, "save correlation", "", "spectrum and fit (*csv)") - if file_name[0]: - exporter.export(str(file_name[0]) + ".csv") - self.statusbar.showMessage(f"Data saved to {str(file_name[0])}") - else: - pass - - def tiff_export_images(self): - file_name = QFileDialog().getSaveFileName(self, "save images", "", "spectrum and fit (*tiff)") - if file_name[0]: - tf.imsave(str(file_name[0]) + ".tiff", np.dstack([self.img1, self.img2]).T) - self.statusbar.showMessage(f"Images saved to {str(file_name[0])}") - else: - pass - - -class LoadingScreen(QtWidgets.QSplashScreen): - def __init__(self): - super(LoadingScreen, self).__init__() - uic.loadUi(os.path.join(ui_path, "uis/animationWindow.ui"), self) - self.setWindowOpacity(0.65) - self.movie = QMovie("uis/animation.gif") - self.label.setMovie(self.movie) - - def mousePressEvent(self, event): - # disable default "click-to-dismiss" behaviour - pass - - def startAnimation(self): - self.movie.start() - self.show() - - def stopAnimation(self): - self.movie.stop() - self.hide() - - -class CompositeScatterPlot(QtWidgets.QMainWindow): - def __init__(self, scatterPoints, fitLine, maskImages, fitEquations, nameTuple): - super(CompositeScatterPlot, self).__init__() - - uic.loadUi(os.path.join(ui_path, "uis/multipleScatterFit.ui"), self) - self.centralwidget.setStyleSheet(open(os.path.join(ui_path, "css/defaultStyle.css")).read()) - - self.scatterPoints = scatterPoints - self.fitLine = fitLine - self.scatterColors = ["r", (0, 115, 0), (4, 186, 186), "c", "w", "k"] - self.fitColors = ["b", "r", "m", "k", "b"] - self.roiNames = list(fitEquations.keys()) - self.fitEqns = list(fitEquations.values()) - self.nameTuple = nameTuple - self.maskImages = maskImages - - # self.scatterViewer.setBackground('w') - # set the graphicslayoutwidget in the ui as canvas - self.canvas = self.scatterViewer.addPlot() - self.canvas.addLegend() - self.canvas.setLabel("bottom", self.nameTuple[0], "counts") - self.canvas.setLabel("left", self.nameTuple[1], "counts") - - # connections - self.actionExport.triggered.connect(self.exportData) - self.actionSave_as_PNG.triggered.connect(self.exportAsPNG) - self.actionGenerate_MultiColor_Mask.triggered.connect(self.generateMultiColorView) - self.actionWhite.triggered.connect(lambda: self.scatterViewer.setBackground("w")) - self.actionBlack.triggered.connect(lambda: self.scatterViewer.setBackground("k")) - - with pg.BusyCursor(): - - for arr, fitline, clr, fitClr, rname, feqn in zip( - self.scatterPoints, self.fitLine, self.scatterColors, self.fitColors, self.roiNames, self.fitEqns - ): - - sctrPoints = [] - for pt in arr: - sctrPoints.append( - {"pos": (pt[0], pt[1]), "data": "id", "size": 3, "pen": pg.mkPen(None), "brush": clr} - ) - - # generate a scatter plot item - self.scattered = pg.ScatterPlotItem(size=4.5, pen=clr, brush=pg.mkBrush(5, 214, 255, 200)) - # set scatter plot data - self.scattered.setPoints(sctrPoints, name=rname) - - # set z value negative to show scatter data behind the fit line - self.scattered.setZValue(-10) - - # add scatter plot to the canvas - self.canvas.addItem(self.scattered) - - # generate plotitem for fit line - self.fitLinePlot = pg.PlotDataItem(pen=pg.mkPen(fitClr, width=4.5)) - - # set line plot data - self.fitLinePlot.setData(fitline, name=feqn) - - # add line plot to the canvas - self.canvas.addItem(self.fitLinePlot) - - def generateMultiColorView(self): - self.multichanneldict = {} - - for n, (colorName, image, rname) in enumerate(zip(cmap_dict.keys(), self.maskImages, self.roiNames)): - low, high = np.min(image), np.max(image) - self.multichanneldict[rname] = { - "ImageName": rname, - "ImageDir": ".", - "Image": image, - "Color": colorName, - "CmapLimits": (low, high), - "Opacity": 1.0, - } - - # print( self.multichanneldict) - self.muli_color_window = MultiChannelWindow(image_dict=self.multichanneldict) - self.muli_color_window.show() - - def exportData(self): - - exporter = pg.exporters.CSVExporter(self.canvas) - # exporter.parameters()['columnMode'] = '(x,y,y,y) for all plots' - file_name = QFileDialog().getSaveFileName(self, "Save CSV Data", "scatter.csv", "image file (*csv)") - if file_name[0]: - exporter.export(str(file_name[0])) - self.statusbar.showMessage(f"Data saved to {str(file_name[0])}") - else: - pass - - def exportAsPNG(self): - file_name = QtWidgets.QFileDialog().getSaveFileName( - self, "Save Image", "image.png", "PNG(*.png);; TIFF(*.tiff);; JPG(*.jpg)" - ) - exporter = pg.exporters.ImageExporter(self.canvas) - - if file_name[0]: - exporter.export(str(file_name[0])) - self.statusbar.showMessage(f"Image saved to {str(file_name[0])}") - else: - pass - - -class MaskSpecViewer(QtWidgets.QMainWindow): - def __init__(self, xanes_stack=None, xrf_map=None, energy=[]): - super(MaskSpecViewer, self).__init__() - uic.loadUi(os.path.join(ui_path, "uis/MaskedView.ui"), self) - - self.xanes_stack = xanes_stack - self.xrf_map = xrf_map - self.energy = energy - self.xrf_map = self.xanes_stack[-1] - self.view_data() - - # connections - self.sldr_xrf_low.valueChanged.connect(self.create_mask) - self.sldr_xrf_high.valueChanged.connect(self.create_mask) - self.pb_apply_mask.clicked.connect(self.apply_mask_to_xanes) - self.pb_export_mask.clicked.connect(self.export_mask) - self.pb_import_mask.clicked.connect(self.import_a_mask) - self.actionLoad_Energy_List.triggered.connect(self.load_energy) - self.actionLoad_XANES_Stack.triggered.connect(self.load_xanes_stack) - self.actionLoad_XRF_Map.triggered.connect(self.load_xrf_map) - - def view_data(self): - - self.xanes_view.setImage(self.xanes_stack) - self.xanes_view.ui.menuBtn.hide() - self.xanes_view.ui.roiBtn.hide() - (self.dim1, self.dim3, self.dim2) = self.xanes_stack.shape - self.xanes_view.setPredefinedGradient("viridis") - self.xanes_view.setCurrentIndex(self.dim1 // 2) - self.statusbar.showMessage("One image from the XANES stack is used as mask") - self.xrf_view.setImage(self.xrf_map) - self.xrf_view.ui.menuBtn.hide() - self.xrf_view.ui.roiBtn.hide() - self.xrf_view.setPredefinedGradient("viridis") - - self.mask_view.ui.menuBtn.hide() - self.mask_view.ui.roiBtn.hide() - - def create_mask(self): - self.threshold_low = np.around(self.sldr_xrf_low.value() * 0.01, 3) - self.threshold_high = np.around(self.sldr_xrf_high.value() * 0.01, 3) - self.sldr_xrf_low.setMaximum(self.sldr_xrf_high.value() + 1) - self.sldr_xrf_high.setMinimum(self.sldr_xrf_low.value() + 1) - self.norm_xrf_map = remove_nan_inf(self.xrf_map) / remove_nan_inf(self.xrf_map.max()) - self.norm_xrf_map[self.norm_xrf_map < self.threshold_low] = 0 - self.norm_xrf_map[self.norm_xrf_map > self.threshold_high] = 0 - self.xrf_view.setImage(self.norm_xrf_map) - self.le_sldr_vals.setText(str(self.threshold_low) + " to " + str(self.threshold_high)) - self.statusbar.showMessage("New Threshold Applied") - self.xrf_mask = np.where(self.norm_xrf_map > 0, self.norm_xrf_map, 0) - self.xrf_mask[self.xrf_mask > 0] = 1 - self.mask_view.setImage(self.xrf_mask) - - def load_xanes_stack(self): - """loading a new xanes stack""" - filename = QFileDialog().getOpenFileName(self, "Select image data", "", "image file(*tiff *tif )") - self.file_name = str(filename[0]) - self.xanes_stack = tf.imread(self.file_name).transpose(0, 2, 1) - self.view_data() - - def load_energy(self): - """To load energy list that will be used for plotting the spectra. - number of stack should match length of energy list""" - - file_name = QFileDialog().getOpenFileName(self, "Open energy list", "", "text file (*.txt)") - - try: - self.energy = np.loadtxt(str(file_name[0])) - logger.info("Energy file loaded") - assert len(self.energy) == self.dim1 - self.view_data() - - except OSError: - logger.error("No File selected") - pass - - def load_xrf_map(self): - """To xrf map for masking. If 3D mean will be taken""" - - filename = QFileDialog().getOpenFileName(self, "Select image data", "", "image file(*tiff *tif )") - self.xrf_file_name = str(filename[0]) - self.xrf_map = tf.imread(self.xrf_file_name) - if self.xrf_map.ndim == 3: - self.xrf_map = self.xrf_map.mean(0).T - - else: - self.xrf_map = self.xrf_map.T - - assert ( - self.dim3, - self.dim2, - ) == self.xrf_map.shape, f"Unexpected image dimensions: {self.xrf_map.shape} vs {(self.dim2,self.dim3)}" - - self.view_data() - self.create_mask() - - def apply_mask_to_xanes(self): - - """Generates a mask with 0 and 1 from the choosen threshold and multply with the xanes stack. - A spectrum will be generated from the new masked stack""" - - self.masked_xanes = self.xanes_stack * self.xrf_mask - self.xanes_view.setImage(self.masked_xanes) - self.xanes_view.setCurrentIndex(self.dim1 // 2) - self.statusbar.showMessage("Mask Applied to XANES") - self.mask_spec = get_mean_spectra(self.masked_xanes) - - if len(self.energy) != 0: - self.xdata = self.energy - else: - self.xdata = np.arange(0, self.dim1) - self.statusbar.showMessage("No Energy List Available; Integer values are used for plotting") - - self.spectrum_view.plot(self.xdata, self.mask_spec, clear=True) - - def import_a_mask(self): - filename = QFileDialog().getOpenFileName(self, "Select image data", "", "image file(*tiff *tif )") - xrf_file_name = str(filename[0]) - self.xrf_mask = tf.imread(xrf_file_name).T - self.statusbar.showMessage("A New Mask Imported") - self.mask_view.setImage(self.xrf_mask) - self.apply_mask_to_xanes() - - def export_mask(self): - try: - file_name = QFileDialog().getSaveFileName(self, "Save image data", "", "image file(*tiff *tif )") - tf.imsave(str(file_name[0]) + ".tiff", self.xrf_mask.T) - logger.info(f"Updated Image Saved: {str(file_name[0])}") - self.statusbar.showMessage("Mask Exported") - except Exception: - logger.error("No file to save") - pass - - -class StackInfo(QtWidgets.QMainWindow): - def __init__(self, text_to_write: str = " "): - super(StackInfo, self).__init__() - uic.loadUi(os.path.join(ui_path, "uis/log.ui"), self) - - self.text_to_write = text_to_write - self.pte_run_cmd.setPlainText(self.text_to_write) - - # connections - self.pb_save_cmd.clicked.connect(self.save_file) - self.pb_clear_cmd.clicked.connect(self.clear_text) - - def save_file(self): - S__File = QFileDialog.getSaveFileName(None, "SaveFile", "/", "txt Files (*.txt)") - - Text = self.pte_run_cmd.toPlainText() - if S__File[0]: - with open(S__File[0], "w") as file: - file.write(Text) - - def clear_text(self): - self.pte_run_cmd.clear() - - -class MultiChannelWindow(QtWidgets.QMainWindow): - def __init__(self, image_dict=None): - super(MultiChannelWindow, self).__init__() - if image_dict is None: - image_dict = {} - uic.loadUi(os.path.join(ui_path, "uis/mutlichannel.ui"), self) - - self.canvas = self.img_view.addPlot(title="") - self.canvas.getViewBox().invertY(True) - self.canvas.setAspectLocked(True) - self.cb_choose_color.addItems([i for i in cmap_dict.keys()]) - - self.image_dict = image_dict - self.buildFromDictionary() - - # connections - self.actionLoad.triggered.connect(self.createMuliColorAndList) - self.actionLoad_Stack.triggered.connect(self.createMuliColorAndList) - self.cb_choose_color.currentTextChanged.connect(self.updateImageDictionary) - self.pb_update_low_high.clicked.connect(self.updateImageDictionary) - self.listWidget.itemClicked.connect(self.editImageProperties) - self.listWidget.itemDoubleClicked.connect(self.showOneImageOnly) - self.pb_show_selected.clicked.connect(self.showOneImageOnly) - self.pb_show_all.clicked.connect(self.showAllItems) - self.actionLoad_State_File.triggered.connect(self.importState) - self.actionSave_State.triggered.connect(self.exportState) - self.actionSave_View.triggered.connect(self.saveImage) - - def buildFromDictionary(self): - if self.image_dict is not None: - self.createMultiColorView(self.image_dict) - self.displayImageNames(self.image_dict) - else: - pass - - def generateImageDictionary(self): - """Creates a dictionary contains image path, color scheme chosen, throshold limits etc. - when user edits the parameters dictionary will be updated and unwrapped for display later. - This dictionary is saved as json file while saving the state. Two image loading options are possible. - User can either select multiple 2D array images or one 3D array (stack)""" - - clickedAction = self.sender() - - if clickedAction.text() == "Load Images": - # multiple images are selected - self.loadMultipleImageFiles() - - elif clickedAction.text() == "Load Stack": - # an image stack is selected - self.loadAsStack() - - def loadMultipleImageFiles(self): - - filter = "TIFF (*.tiff);;TIF (*.tif)" - QtWidgets.QFileDialog().setFileMode(QtWidgets.QFileDialog.ExistingFiles) - # choose mutliple tiff files - names = QtWidgets.QFileDialog().getOpenFileNames(self, "Open files", " ", filter) - if names[0]: - self.image_dict = {} - # select the file directory. Image files are expected to be in the same folder - self.imageDir = os.path.dirname(names[0][0]) - - # create the dictionary - for colorName, image in zip(cmap_dict.keys(), names[0]): - # squeeze to allow with pseudo 3D axis from some tomo recon (eg. 1, 100,100 array) - im_array = np.squeeze(tf.imread(image)) - # set values for thresholding as image min and max - low, high = np.min(im_array), np.max(im_array) - # name of the tiff file is chosen as key for the dictionary, - # inner keys are properties set for that image - im_name = os.path.basename(image) - # construct the dictionary - self.image_dict[f"{os.path.basename(image)}"] = { - "ImageName": im_name, - "ImageDir": self.imageDir, - "Image": im_array, - "Color": colorName, - "CmapLimits": (low, high), - "Opacity": 1.0, - } - else: - pass - - def loadAsStack(self): - """construct the dictionary with image +number as the key. - All other steps are similar to the loadMultipleImageFiles function""" - - filter = "TIFF (*.tiff);;TIF (*.tif)" - file_name = QtWidgets.QFileDialog().getOpenFileName( - self, "Open a Stack", "", "TIFF(*tiff *tif);;all_files (*)", filter - ) - if file_name[0]: - self.imageDir = os.path.dirname(file_name[0]) - self.image_dict = {} - im_stack = np.squeeze(tf.imread(file_name[0])) - # asset the file is a stack - assert im_stack.ndim == 3, "Not a stack" - # construct the dictionary - for n, (colorName, image) in enumerate(zip(cmap_dict.keys(), im_stack)): - low, high = np.min(image), np.max(image) - self.image_dict[f"Image {n+1}"] = { - "ImageName": f"Image {n+1}", - "ImageDir": self.imageDir, - "Image": image, - "Color": colorName, - "CmapLimits": (low, high), - "Opacity": 1.0, - } - - def loadAnImage(self, image, colormap, cmap_limits, opacity=1): - """load single image and colorbar to the widget. This function will be looped for - multiple images later - """ - # get pg image item - img = pg.ImageItem() - # add image to the graphicsview widget - self.canvas.addItem(img) - # set the color map - cmap = pg.ColorMap(pos=np.linspace(0, 1, len(colormap)), color=colormap) - # image = np.squeeze(tf.imread(image_path)) - # set image to the image item with cmap - img.setImage(np.array(image), lut=cmap.getLookupTable(), opacity=opacity) - - # set colorbar for thresholding - bar = pg.ColorBarItem(values=cmap_limits, cmap=cmap, limits=(0, None), orientation="vertical") - bar.setImageItem(img) - # set composition mode to plus for overlaying - img.setCompositionMode(QtGui.QPainter.CompositionMode_Plus) - - def createMultiColorView(self, image_dictionary): - """Function creates multi color image view by taking image - data and parameters from the dictionary""" - - # clear the plots and list in case of re-loading - self.canvas.clear() - self.listWidget.clear() - - # display individual images in for loop - for path_and_color in image_dictionary.values(): - self.loadAnImage( - path_and_color["Image"], - cmap_dict[path_and_color["Color"]], - path_and_color["CmapLimits"], - path_and_color["Opacity"], - ) - - def showOneImageOnly(self): - editItem = self.listWidget.currentItem() - editRow = self.listWidget.currentRow() - for i in range(self.listWidget.count()): - if self.listWidget.item(i) == editItem: - editItemName = self.listWidget.item(i).text().split(",")[0] - self.image_dict[editItemName]["Opacity"] = 1 - - elif self.listWidget.item(i) != editItem: - editItemName = self.listWidget.item(i).text().split(",")[0] - self.image_dict[editItemName]["Opacity"] = 0 - - self.createMultiColorView(self.image_dict) - self.displayImageNames(self.image_dict) - self.listWidget.setCurrentRow(editRow) - - def showAllItems(self): - # editItem = self.listWidget.currentItem() - editRow = self.listWidget.currentRow() - for i in range(self.listWidget.count()): - editItemName = self.listWidget.item(i).text().split(",")[0] - self.image_dict[editItemName]["Opacity"] = 1 - - self.createMultiColorView(self.image_dict) - self.displayImageNames(self.image_dict) - self.listWidget.setCurrentRow(editRow) - - def displayImageNames(self, image_dictionary): - """Populate the list widget table with image name and color used to plot, - using image dictionary input""" - - for im_name, vals in image_dictionary.items(): - self.listWidget.addItem(f"{im_name},{vals['Color']}") - self.listWidget.setCurrentRow(0) - - def createMuliColorAndList(self): - """Finally Load Images and poplulate the list widget from the dictionary""" - with pg.BusyCursor(): # gives the circle showing gui is doing something - self.generateImageDictionary() - if self.image_dict: - self.createMultiColorView(self.image_dict) - self.displayImageNames(self.image_dict) - - else: - pass - - def sliderSetUp(self, im_array): - """Setting the slider min and max from image values""" - - low = (np.min(im_array) / np.max(im_array)) * 100 - self.sldr_low.setMaximum(100) - self.sldr_low.setMinimum(low) - self.sldr_high.setMaximum(100) - self.sldr_high.setMinimum(low) - - def editImageProperties(self, item): - """function to control the assigned properties such as color, - threshold limits, opacity etc of a single image selected using the list widget item""" - - editItem = item.text() - # get the dictionary key from item text - editItemName = editItem.split(",")[0] - editItemColor = editItem.split(",")[1] - im_array = self.image_dict[editItemName]["Image"] - self.sliderSetUp(im_array) - setValLow = (self.image_dict[editItemName]["CmapLimits"][0] * 100) / np.max(im_array) - setValHigh = (self.image_dict[editItemName]["CmapLimits"][1] * 100) / np.max(im_array) - setOpacity = self.image_dict[editItemName]["Opacity"] * 100 - self.sldr_low.setValue(int(setValLow)) - self.sldr_high.setValue(int(setValHigh)) - self.sldr_opacity.setValue(int(setOpacity)) - self.low_high_vals.setText(f"low:{self.sldr_low.value()}," f"high:{self.sldr_high.value()}") - self.cb_choose_color.setCurrentText(editItemColor) - - def updateImageDictionary(self): - newColor = self.cb_choose_color.currentText() - editItem = self.listWidget.currentItem().text() - editRow = self.listWidget.currentRow() - editItemName = editItem.split(",")[0] - self.imageDir = self.image_dict[editItemName]["ImageDir"] - im_array = self.image_dict[editItemName]["Image"] - self.sliderSetUp(im_array) - cmap_limits = ( - self.sldr_low.value() * np.max(im_array) / 100, - self.sldr_high.value() * np.max(im_array) / 100, - ) - self.low_high_vals.setText(f"low:{cmap_limits[0]:.3f},high:{cmap_limits[1]:.3f}") - opacity = self.sldr_opacity.value() / 100 - self.opacity_val.setText(str(opacity)) - self.image_dict[editItemName] = { - "ImageName": editItemName, - "ImageDir": self.imageDir, - "Image": im_array, - "Color": newColor, - "CmapLimits": cmap_limits, - "Opacity": opacity, - } - - self.createMultiColorView(self.image_dict) - self.displayImageNames(self.image_dict) - self.listWidget.setCurrentRow(editRow) - - def exportState(self): - - file_name = QtWidgets.QFileDialog().getSaveFileName( - self, "Save Current State", "multicolor_params.json", "json file(*json)" - ) - """ - for val in self.image_dict.values(): - val['CmapLimits'] = json.dumps(str(val['CmapLimits'])) - """ - - if file_name[0]: - - with open(f"{file_name[0]}", "w") as fp: - json.dump(self.image_dict, fp, indent=4, cls=jsonEncoder) - - else: - pass - - def importState(self): - file_name = QtWidgets.QFileDialog().getOpenFileName( - self, "Open a State File", "", "json file(*json);;all_files (*)" - ) - if file_name[0]: - with open(file_name[0], "r") as fp: - self.image_dict = json.load(fp) - - self.createMultiColorView(self.image_dict) - self.displayImageNames(self.image_dict) - else: - pass - - def saveImage(self): - file_name = QtWidgets.QFileDialog().getSaveFileName( - self, "Save Image", "multicolor_image.png", "PNG(*.png);; TIFF(*.tiff);; JPG(*.jpg)" - ) - exporter = pg.exporters.ImageExporter(self.canvas.getViewBox()) - exporter.export(file_name[0]) - - -""" Helper Functions""" - - -def get_xrf_data(h="h5file"): - """ - get xrf stack from h5 data generated at NSLS-II beamlines - - Arguments: - h5/hdf5 file - - Returns: - norm_xrf_stack - xrf stack image normalized with Io - mono_e - excitation enegy used for xrf - beamline - identity of the beamline - Io_avg - an average Io value, used before taking log - - """ - - f = h5py.File(h, "r") - - if list(f.keys())[0] == "xrfmap": - logger.info("Data from HXN/TES/SRX") - beamline = f["xrfmap/scan_metadata"].attrs["scan_instrument_id"] - - try: - - beamline_scalar = {"HXN": 2, "SRX": 0, "TES": 0} - - if beamline in beamline_scalar.keys(): - - Io = np.array(f["xrfmap/scalers/val"])[:, :, beamline_scalar[beamline]] - raw_xrf_stack = np.array(f["xrfmap/detsum/counts"]) - norm_xrf_stack = raw_xrf_stack - Io_avg = int(remove_nan_inf(Io).mean()) - else: - logger.error("Unknown Beamline Scalar") - except Exception: - logger.warning("Unknown Scalar: Raw Detector count in use") - norm_xrf_stack = np.array(f["xrfmap/detsum/counts"]) - - elif list(f.keys())[0] == "xrmmap": - logger.info("Data from XFM") - beamline = "XFM" - raw_xrf_stack = np.array(f["xrmmap/mcasum/counts"]) - Io = np.array(f["xrmmap/scalars/I0"]) - norm_xrf_stack = raw_xrf_stack - Io_avg = int(remove_nan_inf(Io).mean()) - - else: - logger.error("Unknown Data Format") - - try: - mono_e = int(f["xrfmap/scan_metadata"].attrs["instrument_mono_incident_energy"] * 1000) - logger.info("Excitation energy was taken from the h5 data") - - except Exception: - mono_e = 12000 - logger.info(f"Unable to get Excitation energy from the h5 data; using default value {mono_e} KeV") - - return remove_nan_inf(norm_xrf_stack.transpose((2, 0, 1))), mono_e + 1500, beamline, Io_avg - - -def remove_nan_inf(im): - im = np.array(im, dtype=np.float32) - im[np.isnan(im)] = 0 - im[np.isinf(im)] = 0 - return im - - -def rebin_image(im, bin_factor): - arrx, arry = np.shape(im) - if arrx / bin_factor != int or arrx / bin_factor != int: - logger.error("Invalid Binning") - - else: - shape = (arrx / bin_factor, arry / bin_factor) - return im.reshape(shape).mean(-1).mean(1) - - -def remove_hot_pixels(image_array, NSigma=5): - image_array = remove_nan_inf(image_array) - a, b, c = np.shape(image_array) - img_stack2 = np.zeros((a, b, c)) - for i in range(a): - im = image_array[i, :, :] - im[abs(im) > np.std(im) * NSigma] = im.mean() - img_stack2[i, :, :] = im - return img_stack2 - - -def smoothen(image_array, w_size=5): - a, b, c = np.shape(image_array) - spec2D_Matrix = np.reshape(image_array, (a, (b * c))) - smooth2D_Matrix = savgol_filter(spec2D_Matrix, w_size, w_size - 2, axis=0) - return remove_nan_inf(np.reshape(smooth2D_Matrix, (a, b, c))) - - -def resize_stack(image_array, upscaling=False, scaling_factor=2): - en, im1, im2 = np.shape(image_array) - - if upscaling: - im1_ = im1 * scaling_factor - im2_ = im2 * scaling_factor - img_stack_resized = resize(image_array, (en, im1_, im2_)) - - else: - im1_ = int(im1 / scaling_factor) - im2_ = int(im2 / scaling_factor) - img_stack_resized = resize(image_array, (en, im1_, im2_)) - - return img_stack_resized - - -def normalize(image_array, norm_point=-1): - norm_stack = image_array / image_array[norm_point] - return remove_nan_inf(norm_stack) - - -def remove_edges(image_array): - # z, x, y = np.shape(image_array) - return image_array[:, 1:-1, 1:-1] - - -def background_value(image_array): - img = image_array.mean(0) - img_h = img.mean(0) - img_v = img.mean(1) - h = np.gradient(img_h) - v = np.gradient(img_v) - bg = np.min([img_h[h == h.max()], img_v[v == v.max()]]) - return bg - - -def background_subtraction(img_stack, bg_percentage=10): - img_stack = remove_nan_inf(img_stack) - a, b, c = np.shape(img_stack) - ref_image = np.reshape(img_stack.mean(0), (b * c)) - bg_ratio = int((b * c) * 0.01 * bg_percentage) - bg_ = np.max(sorted(ref_image)[0:bg_ratio]) - bged_img_stack = img_stack - bg_[:, np.newaxis, np.newaxis] - return bged_img_stack - - -def background_subtraction2(img_stack, bg_percentage=10): - img_stack = remove_nan_inf(img_stack) - a, b, c = np.shape(img_stack) - bg_ratio = int((b * c) * 0.01 * bg_percentage) - bged_img_stack = img_stack.copy() - - for n, img in enumerate(img_stack): - bg_ = np.max(sorted(img.flatten())[0:bg_ratio]) - print(bg_) - bged_img_stack[n] = img - bg_ - - return remove_nan_inf(bged_img_stack) - - -def background1(img_stack): - img = img_stack.sum(0) - img_h = img.mean(0) - img_v = img.mean(1) - h = np.gradient(img_h) - v = np.gradient(img_v) - bg = np.min([img_h[h == h.max()], img_v[v == v.max()]]) - return bg - - -def get_sum_spectra(image_array): - spec = np.sum(image_array, axis=(1, 2)) - return spec - - -def get_mean_spectra(image_array): - spec = np.mean(image_array, axis=(1, 2)) - return spec - - -def flatten_(image_array): - z, x, y = np.shape(image_array) - flat_array = np.reshape(image_array, (x * y, z)) - return flat_array - - -def image_to_pandas(image_array): - a, b, c = np.shape(image_array) - im_array = np.reshape(image_array, ((b * c), a)) - a, b = im_array.shape - df = pd.DataFrame( - data=im_array[:, :], columns=["e" + str(i) for i in range(b)], index=["s" + str(i) for i in range(a)] - ) - return df - - -def image_to_pandas2(image_array): - a, b, c = np.shape(image_array) - im_array = np.reshape(image_array, (a, (b * c))) - a, b = im_array.shape - df = pd.DataFrame( - data=im_array[:, :], index=["e" + str(i) for i in range(a)], columns=["s" + str(i) for i in range(b)] - ) - return df - - -def neg_log(image_array): - absorb = -1 * np.log(image_array) - return remove_nan_inf(absorb) - - -def clean_stack(img_stack, auto_bg=False, bg_percentage=5): - a, b, c = np.shape(img_stack) - - if auto_bg is True: - bg_ = background1(img_stack) - - else: - sum_spec = (img_stack.sum(1)).sum(1) - ref_stk_num = np.where(sum_spec == sum_spec.max())[-1] - - ref_image = np.reshape(img_stack[ref_stk_num], (b * c)) - bg_ratio = int((b * c) * 0.01 * bg_percentage) - bg_ = np.max(sorted(ref_image)[0:bg_ratio]) - - bg = np.where(img_stack[ref_stk_num] > bg_, img_stack[ref_stk_num], 0) - bg2 = np.where(bg < bg_, bg, 1) - - bged_img_stack = img_stack * bg2 - - return remove_nan_inf(bged_img_stack) - - -def subtractBackground(im_stack, bg_region): - if bg_region.ndim == 3: - bg_region_ = np.mean(bg_region, axis=(1, 2)) - - elif bg_region.ndim == 2: - bg_region_ = np.mean(bg_region, axis=1) - - else: - bg_region_ = bg_region - - return im_stack - bg_region_[:, np.newaxis, np.newaxis] - - -def classify(img_stack, correlation="Pearson"): - img_stack_ = img_stack - a, b, c = np.shape(img_stack_) - norm_img_stack = normalize(img_stack_) - f = np.reshape(norm_img_stack, (a, (b * c))) - - max_x, max_y = np.where(norm_img_stack.sum(0) == (norm_img_stack.sum(0)).max()) - ref = norm_img_stack[:, int(max_x), int(max_y)] - corr = np.zeros(len(f.T)) - for s in range(len(f.T)): - if correlation == "Kendall": - r, p = stats.kendalltau(ref, f.T[s]) - elif correlation == "Pearson": - r, p = stats.pearsonr(ref, f.T[s]) - - corr[s] = r - - cluster_image = np.reshape(corr, (b, c)) - return (cluster_image**3), img_stack_ - - -def correlation_kmeans(img_stack, n_clusters, correlation="Pearson"): - img, bg_image = classify(img_stack, correlation) - img[np.isnan(img)] = -99999 - X = img.reshape((-1, 1)) - k_means = sc.KMeans(n_clusters) - k_means.fit(X) - - X_cluster = k_means.labels_ - X_cluster = X_cluster.reshape(img.shape) + 1 - - return X_cluster - - -def cluster_stack( - im_array, method="KMeans", n_clusters_=4, decomposed=False, decompose_method="PCA", decompose_comp=2 -): - a, b, c = im_array.shape - - if method == "Correlation-Kmeans": - - X_cluster = correlation_kmeans(im_array, n_clusters_, correlation="Pearson") - - else: - - methods = { - "MiniBatchKMeans": sc.MiniBatchKMeans, - "KMeans": sc.KMeans, - "MeanShift": sc.MeanShift, - "Spectral Clustering": sc.SpectralClustering, - "Affinity Propagation": sc.AffinityPropagation, - } - - if decomposed: - im_array = denoise_with_decomposition(im_array, method_=decompose_method, n_components=decompose_comp) - - flat_array = np.reshape(im_array, (a, (b * c))) - init_cluster = methods[method](n_clusters=n_clusters_) - init_cluster.fit(np.transpose(flat_array)) - X_cluster = init_cluster.labels_.reshape(b, c) + 1 - - decon_spectra = np.zeros((a, n_clusters_)) - decon_images = np.zeros((n_clusters_, b, c)) - - for i in range(n_clusters_): - mask_i = np.where(X_cluster == (i + 1), X_cluster, 0) - spec_i = get_sum_spectra(im_array * mask_i) - decon_spectra[:, i] = spec_i - decon_images[i] = im_array.sum(0) * mask_i - - return decon_images, X_cluster, decon_spectra - - -def kmeans_variance(im_array): - a, b, c = im_array.shape - flat_array = np.reshape(im_array, (a, (b * c))) - var = np.arange(24) - clust_n = np.arange(24) + 2 - - for clust in var: - init_cluster = sc.KMeans(n_clusters=int(clust + 2)) - init_cluster.fit(np.transpose(flat_array)) - var_ = init_cluster.inertia_ - var[clust] = np.float64(var_) - - kmeans_var_plot = pg.plot( - clust_n, var, title="KMeans Variance", pen=pg.mkPen("y", width=2, style=QtCore.Qt.DotLine), symbol="o" - ) - kmeans_var_plot.setLabel("bottom", "Cluster Number") - kmeans_var_plot.setLabel("left", "Sum of squared distances") - - -def pca_scree(im_stack): - new_image = im_stack.transpose(2, 1, 0) - x, y, z = np.shape(new_image) - img_ = np.reshape(new_image, (x * y, z)) - pca = sd.PCA(z) - pca.fit(img_) - # var = pca.explained_variance_ratio_ - var = pca.singular_values_ - - pca_scree_plot = pg.plot( - var[:24], title="PCA Scree Plot", pen=pg.mkPen("y", width=2, style=QtCore.Qt.DotLine), symbol="o" - ) - pca_scree_plot.addLine(y=0) - pca_scree_plot.setLabel("bottom", "Component Number") - pca_scree_plot.setLabel("left", "Singular Values") - - -def decompose_stack(im_stack, decompose_method="PCA", n_components_=3): - new_image = im_stack.transpose(2, 1, 0) - x, y, z = np.shape(new_image) - img_ = np.reshape(new_image, (x * y, z)) - methods_dict = { - "PCA": sd.PCA, - "IncrementalPCA": sd.IncrementalPCA, - "NMF": sd.NMF, - "FastICA": sd.FastICA, - "DictionaryLearning": sd.MiniBatchDictionaryLearning, - "FactorAnalysis": sd.FactorAnalysis, - "TruncatedSVD": sd.TruncatedSVD, - } - - _mdl = methods_dict[decompose_method](n_components=n_components_) - - ims = (_mdl.fit_transform(img_).reshape(x, y, n_components_)).transpose(2, 1, 0) - spcs = _mdl.components_.transpose() - decon_spetra = np.zeros((z, n_components_)) - decom_map = np.zeros((ims.shape)) - - for i in range(n_components_): - f = ims.copy()[i] - f[f < 0] = 0 - spec_i = ((new_image.T * f).sum(1)).sum(1) - decon_spetra[:, i] = spec_i - - f[f > 0] = i + 1 - decom_map[i] = f - decom_map = decom_map.sum(0) - - return np.float32(ims), spcs, decon_spetra, decom_map - - -def denoise_with_decomposition(img_stack, method_="PCA", n_components=4): - new_image = img_stack.transpose(2, 1, 0) - x, y, z = np.shape(new_image) - img_ = np.reshape(new_image, (x * y, z)) - - methods_dict = { - "PCA": sd.PCA, - "IncrementalPCA": sd.IncrementalPCA, - "NMF": sd.NMF, - "FastICA": sd.FastICA, - "DictionaryLearning": sd.DictionaryLearning, - "FactorAnalysis": sd.FactorAnalysis, - "TruncatedSVD": sd.TruncatedSVD, - } - - decomposed = methods_dict[method_](n_components=n_components) - - ims = (decomposed.fit_transform(img_).reshape(x, y, n_components)).transpose(2, 1, 0) - ims[ims < 0] = 0 - ims[ims > 0] = 1 - mask = ims.sum(0) - mask[mask > 1] = 1 - # mask = uniform_filter(mask) - filtered = img_stack * mask - # plt.figure() - # plt.imshow(filtered.sum(0)) - # plt.title('background removed') - # plt.show() - return remove_nan_inf(filtered) - - -def interploate_E(refs, e): - n = np.shape(refs)[1] - refs = np.array(refs) - ref_e = refs[:, 0] - ref = refs[:, 1:n] - all_ref = [] - for i in range(n - 1): - ref_i = np.interp(e, ref_e, ref[:, i]) - all_ref.append(ref_i) - return np.array(all_ref) - - -def getStats(spec, fit, num_refs=2): - stats = {} - - r_factor = (np.sum(spec - fit) ** 2) / np.sum(spec**2) - stats["R_Factor"] = np.around(r_factor, 5) - - y_mean = np.sum(spec) / len(spec) - SS_tot = np.sum((spec - y_mean) ** 2) - SS_res = np.sum((spec - fit) ** 2) - r_square = 1 - (SS_res / SS_tot) - stats["R_Square"] = np.around(r_square, 4) - - chisq = np.sum((spec - fit) ** 2) - stats["Chi_Square"] = np.around(chisq, 5) - - red_chisq = chisq / (len(spec) - num_refs) - stats["Reduced Chi_Square"] = red_chisq - - return stats - - -def xanes_fitting_1D(spec, e_list, refs, method="NNLS", alphaForLM=0.01): - """Linear combination fit of image data with reference standards""" - - int_refs = interploate_E(refs, e_list) - - if method == "NNLS": - coeffs, r = opt.nnls(int_refs.T, spec) - - elif method == "LASSO": - lasso = linear_model.Lasso(positive=True, alpha=alphaForLM) # lowering alpha helps with 1D fits - fit_results = lasso.fit(int_refs.T, spec) - coeffs = fit_results.coef_ - - elif method == "RIDGE": - ridge = linear_model.Ridge(alpha=alphaForLM) - fit_results = ridge.fit(int_refs.T, spec) - coeffs = fit_results.coef_ - - fit = coeffs @ int_refs - stats = getStats(spec, fit, num_refs=np.min(np.shape(int_refs.T))) - - return stats, coeffs - - -def xanes_fitting(im_stack, e_list, refs, method="NNLS", alphaForLM=0.1, binStack=False): - """Linear combination fit of image data with reference standards""" - - if binStack: - im_stack = resize_stack(im_stack, scaling_factor=4) - - en, im1, im2 = np.shape(im_stack) - im_array = im_stack.reshape(en, im1 * im2) - coeffs_arr = [] - r_factor_arr = [] - # lasso = linear_model.Lasso(positive=True, alpha=alphaForLM) - for n, i in enumerate(range(im1 * im2)): - stats, coeffs = xanes_fitting_1D(im_array[:, i], e_list, refs, method=method, alphaForLM=alphaForLM) - coeffs_arr.append(coeffs) - r_factor_arr.append(stats["R_Factor"]) - - abundance_map = np.reshape(coeffs_arr, (im1, im2, -1)) - r_factor_im = np.reshape(r_factor_arr, (im1, im2)) - - return abundance_map, r_factor_im, np.mean(coeffs_arr, axis=0) - - -def xanes_fitting_Line(im_stack, e_list, refs, method="NNLS", alphaForLM=0.05): - """Linear combination fit of image data with reference standards""" - en, im1, im2 = np.shape(im_stack) - im_array = np.mean(im_stack, 2) - coeffs_arr = [] - meanStats = {"R_Factor": 0, "R_Square": 0, "Chi_Square": 0, "Reduced Chi_Square": 0} - - for i in range(im1): - stats, coeffs = xanes_fitting_1D(im_array[:, i], e_list, refs, method=method, alphaForLM=alphaForLM) - coeffs_arr.append(coeffs) - for key in stats.keys(): - meanStats[key] += stats[key] - - for key, vals in meanStats.items(): - meanStats[key] = np.around((vals / im1), 5) - - return meanStats, np.mean(coeffs_arr, axis=0) - - -def xanes_fitting_Binned(im_stack, e_list, refs, method="NNLS", alphaForLM=0.05): - """Linear combination fit of image data with reference standards""" - - im_stack = resize_stack(im_stack, scaling_factor=10) - # use a simple filter to find threshold value - val = filters.threshold_otsu(im_stack[-1]) - en, im1, im2 = np.shape(im_stack) - im_array = im_stack.reshape(en, im1 * im2) - coeffs_arr = [] - meanStats = {"R_Factor": 0, "R_Square": 0, "Chi_Square": 0, "Reduced Chi_Square": 0} - - specs_fitted = 0 - total_spec = im1 * im2 - for i in range(total_spec): - spec = im_array[:, i] - # do not fit low intensity/background regions - if spec[-1] > val: - specs_fitted += 1 - stats, coeffs = xanes_fitting_1D(spec / spec[-1], e_list, refs, method=method, alphaForLM=alphaForLM) - coeffs_arr.append(coeffs) - for key in stats.keys(): - meanStats[key] += stats[key] - else: - pass - - for key, vals in meanStats.items(): - meanStats[key] = np.around((vals / specs_fitted), 6) - # print(f"{specs_fitted}/{total_spec}") - return meanStats, np.mean(coeffs_arr, axis=0) - - -def create_df_from_nor(athenafile="fe_refs.nor"): - """create pandas dataframe from athena nor file, first column - is energy and headers are sample names""" - - refs = np.loadtxt(athenafile) - n_refs = refs.shape[-1] - skip_raw_n = n_refs + 6 - - df = pd.read_table( - athenafile, delim_whitespace=True, skiprows=skip_raw_n, header=None, usecols=np.arange(0, n_refs) - ) - df2 = pd.read_table( - athenafile, delim_whitespace=True, skiprows=skip_raw_n - 1, usecols=np.arange(0, n_refs + 1) - ) - new_col = df2.columns.drop("#") - df.columns = new_col - return df, list(new_col) - - -def create_df_from_nor_try2(athenafile="fe_refs.nor"): - """create pandas dataframe from athena nor file, first column - is energy and headers are sample names""" - - refs = np.loadtxt(athenafile) - n_refs = refs.shape[-1] - df_refs = pd.DataFrame(refs) - - df = pd.read_csv(athenafile, header=None) - new_col = list((str(df.iloc[n_refs + 5].values)).split(" ")[2::2]) - df_refs.columns = new_col - - return df_refs, list(new_col) - - -def energy_from_logfile(logfile="maps_log_tiff.txt"): - df = pd.read_csv(logfile, header=None, delim_whitespace=True, skiprows=9) - return df[9][df[7] == "energy"].values.astype(float) - - -def xanesNormalization( - e, mu, e0=7125, step=None, nnorm=2, nvict=0, pre1=None, pre2=-50, norm1=100, norm2=None, guess=False -): - if guess: - result = preedge(e, mu, e0, step=step, nnorm=nnorm, nvict=nvict) - - return result["pre1"], result["pre2"], result["norm1"], result["norm2"] - - else: - result = preedge(e, mu, e0, step, nnorm, nvict, pre1, pre2, norm1, norm2) - - return result["pre_edge"], result["post_edge"], result["norm"] - - -def xanesNormStack( - e_list, im_stack, e0=7125, step=None, nnorm=2, nvict=0, pre1=None, pre2=-50, norm1=100, norm2=None -): - - en, im1, im2 = np.shape(im_stack) - im_array = im_stack.reshape(en, im1 * im2) - normedStackArray = np.zeros_like(im_array) - - for i in range(im1 * im2): - pre_line, post_line, normXANES = xanesNormalization( - e_list, - im_array[:, i], - e0=e0, - step=step, - nnorm=nnorm, - nvict=nvict, - pre1=pre1, - pre2=pre2, - norm1=norm1, - norm2=norm2, - guess=False, - ) - normedStackArray[:, i] = normXANES - - return remove_nan_inf(np.reshape(normedStackArray, (en, im1, im2))) - - -def align_stack( - stack_img, ref_image_void=True, ref_stack=None, transformation=StackReg.TRANSLATION, reference="previous" -): - - """Image registration flow using pystack reg""" - - # all the options are in one function - - sr = StackReg(transformation) - - if ref_image_void: - tmats_ = sr.register_stack(stack_img, reference=reference) - - else: - tmats_ = sr.register_stack(ref_stack, reference=reference) - # out_ref = sr.transform_stack(ref_stack) - - out_stk = sr.transform_stack(stack_img, tmats=tmats_) - return np.float32(out_stk), tmats_ - - -def align_simple(stack_img, transformation=StackReg.TRANSLATION, reference="previous"): - - sr = StackReg(transformation) - tmats_ = sr.register_stack(stack_img, reference="previous") - for i in range(10): - out_stk = sr.transform_stack(stack_img, tmats=tmats_) - import time - - time.sleep(2) - return np.float32(out_stk) - - -def align_with_tmat(stack_img, tmat_file, transformation=StackReg.TRANSLATION): - - sr = StackReg(transformation) - out_stk = sr.transform_stack(stack_img, tmats=tmat_file) - return np.float32(out_stk) - - -def align_stack_iter( - stack, - ref_stack_void=True, - ref_stack=None, - transformation=StackReg.TRANSLATION, - method=("previous", "first"), - max_iter=2, -): - if ref_stack_void: - ref_stack = stack - - for i in range(max_iter): - sr = StackReg(transformation) - for ii in range(len(method)): - print(ii, method[ii]) - tmats = sr.register_stack(ref_stack, reference=method[ii]) - ref_stack = sr.transform_stack(ref_stack) - stack = sr.transform_stack(stack, tmats=tmats) - - return np.float32(stack) - - -def applyMaskGetMeanSpectrum(im_stack, mask): - """A 2d mask to multiply with the 3d xanes stack and returns mean spectrum""" - - masked_stack = im_stack * mask - return get_mean_spectra(masked_stack) - - -def modifyStack( - raw_stack, - normalizeStack=False, - normToPoint=-1, - applySmooth=False, - smoothWindowSize=3, - applyThreshold=False, - thresholdValue=0, - removeOutliers=False, - nSigmaOutlier=3, - applyTranspose=False, - transposeVals=(0, 1, 2), - applyCrop=False, - cropVals=(0, 1, 2), - removeEdges=False, - resizeStack=False, - upScaling=False, - binFactor=2, -): - - """A giant function to modify the stack with many possible operations. - all the changes can be saved to a jason file as a config file. Enabling and - distabling the sliders is a problem""" - - """ - normStack = normalize(raw_stack, norm_point=normToPoint) - smoothStack = smoothen(raw_stack, w_size= smoothWindowSize) - thresholdStack = clean_stack(raw_stack, auto_bg=False, bg_percentage = thresholdValue) - outlierStack = remove_hot_pixels(raw_stack, NSigma=nSigmaOutlier) - transposeStack = np.transpose(raw_stack, transposeVals) - croppedStack = raw_stack[cropVals] - edgeStack = remove_edges(raw_stack) - binnedStack = resize_stack(raw_stack,upscaling=upScaling,scaling_factor=binFactor) - - """ - - if removeOutliers: - modStack = remove_hot_pixels(raw_stack, NSigma=nSigmaOutlier) - - else: - modStack = raw_stack - - if applyThreshold: - modStack = clean_stack(modStack, auto_bg=False, bg_percentage=thresholdValue) - - else: - pass - - if applySmooth: - modStack = smoothen(modStack, w_size=smoothWindowSize) - - else: - pass - - if applyTranspose: - modStack = np.transpose(modStack, transposeVals) - - else: - pass - - if applyCrop: - modStack = modStack[cropVals] - - else: - pass - - if normalizeStack: - modStack = normalize(raw_stack, norm_point=normToPoint) - else: - pass - - -def start_xmidas(): - def formatter(prog): - # Set maximum width such that printed help mostly fits in the RTD theme code block (documentation). - return argparse.RawDescriptionHelpFormatter(prog, max_help_position=20, width=90) - - parser = argparse.ArgumentParser( - description=f"XMidas: v{__version__}", - formatter_class=formatter, - ) - parser.parse_args() - - logger.setLevel(logging.INFO) - formatter = logging.Formatter(fmt="%(asctime)s : %(levelname)s : %(message)s") - stream_handler = logging.StreamHandler() - stream_handler.setFormatter(formatter) - stream_handler.setLevel(logging.INFO) - if logger.hasHandlers(): - logger.handlers.clear() - logger.addHandler(stream_handler) - - if version.parse(PYQT_VERSION_STR) >= version.parse("5.14"): - QApplication.setAttribute(QtCore.Qt.HighDpiScaleFactorRoundingPolicy.PassThrough) - - app = QtWidgets.QApplication(sys.argv) - # app.setAttribute(QtCore.Qt.AA_Use96Dpi) - window = midasWindow() - window.show() - sys.exit(app.exec_()) - - -if __name__ == "__main__": - start_xmidas() +# -*- coding: utf-8 -*- + +# Author: Ajith Pattammattel +# First Version on:06-23-2020 + +import argparse +import logging +import sys +import webbrowser +import traceback +import os +import json +import scipy.stats as stats +import numpy as np +import pandas as pd +import tifffile as tf +import pyqtgraph as pg +import pyqtgraph.exporters +from glob import glob +from pyqtgraph import plot +from itertools import combinations +from scipy.stats import linregress +from packaging import version + +from PyQt5 import QtWidgets, QtCore, QtGui, uic, QtTest +from PyQt5.QtGui import QMovie +from PyQt5.QtWidgets import QMessageBox, QFileDialog, QApplication +from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot, QRunnable, QThreadPool, PYQT_VERSION_STR + +from utils import * + +#from . import __version__ + +logger = logging.getLogger() +try: + import cv2 # noqa: F401 +except Exception: + logger.warning("openCV module not found") + pass +if hasattr(QtCore.Qt, "AA_EnableHighDpiScaling"): + QtWidgets.QApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling, True) + +if hasattr(QtCore.Qt, "AA_UseHighDpiPixmaps"): + QtWidgets.QApplication.setAttribute(QtCore.Qt.AA_UseHighDpiPixmaps, True) + +ui_path = os.path.dirname(os.path.abspath(__file__)) + +# global settings for pyqtgraph plot and image colormaps +pg.setConfigOption("imageAxisOrder", "row-major") +cmap_names = ["CET-L13", "CET-L14", "CET-L15"] +cmap_combo = combinations(cmap_names, 2) +cmap_label1 = ["red", "green", "blue"] +cmap_label2 = ["yellow", "magenta", "cyan"] +cmap_dict = {} +for i, name in zip(cmap_names, cmap_label1): + cmap_dict[name] = pg.colormap.get(i).getLookupTable(alpha=True) + +for i, name in zip(cmap_combo, cmap_label2): + cmap_dict[name] = pg.colormap.get(i[0]).getLookupTable(alpha=True) + pg.colormap.get(i[1]).getLookupTable( + alpha=True + ) + cmap_dict[name][:, 3] = 255 + + grey = ( + pg.colormap.get("CET-L13").getLookupTable(alpha=True) + + pg.colormap.get("CET-L14").getLookupTable(alpha=True) + + pg.colormap.get("CET-L15").getLookupTable(alpha=True) + ) + + grey[:, 3] = 255 + cmap_dict["grey"] = grey + + +class jsonEncoder(json.JSONEncoder): + def default(self, obj): + if isinstance(obj, np.integer): + return int(obj) + elif isinstance(obj, np.floating): + return float(obj) + elif isinstance(obj, np.ndarray): + return obj.tolist() + else: + return super(jsonEncoder, self).default(obj) + + +class midasWindow(QtWidgets.QMainWindow): + def __init__(self, im_stack=None, energy=[], refs=[]): + super(midasWindow, self).__init__() + uic.loadUi(os.path.join(ui_path, "uis/midasMainwindow.ui"), self) + self.im_stack = im_stack + self.energy = energy + self.refs = refs + self.loaded_tranform_file = [] + self.image_roi2_flag = False + self.refStackAvailable = False + self.isAReload = False + self.plotWidth = 2 + self.stackStatusDict = {} + + self.user_wd = os.path.expanduser("~") + # self.user_config_path = os.path.join(ui_path,"user_config.json") + + # if not os.path.exists(self.user_config_path): + + # with open(f"{self.user_config_path}", "w") as fp: + # json.dump(self.user_config, fp, indent=4) + + self.plt_colors = [ + "g", + "r", + "c", + "m", + "y", + "w", + "b", + pg.mkPen(70, 5, 80), + pg.mkPen(255, 85, 130), + pg.mkPen(0, 85, 130), + pg.mkPen(255, 170, 60), + ] * 3 + # window style + self.actionDarkMode.triggered.connect(self.darkMode) + self.actionDefault.triggered.connect(self.defaultMode) + self.actionModern.triggered.connect(self.modernMode) + + # self.setToolTipsVisible(True) + for menuItem in self.findChildren(QtWidgets.QMenu): + menuItem.setToolTipsVisible(True) + + # plotview options + self.actionWhite.triggered.connect(lambda: self.spectrum_view.setBackground("w")) + self.actionRed.triggered.connect(lambda: self.spectrum_view.setBackground("r")) + self.actionYellow.triggered.connect(lambda: self.spectrum_view.setBackground("y")) + self.actionBlue.triggered.connect(lambda: self.spectrum_view.setBackground("b")) + self.actionBlack.triggered.connect(lambda: self.spectrum_view.setBackground((0, 0, 0))) + + self.actn1.triggered.connect(lambda: self.setPlotLineWidth(int(self.actn1.text()))) + self.actn2.triggered.connect(lambda: self.setPlotLineWidth(int(self.actn2.text()))) + self.actn3.triggered.connect(lambda: self.setPlotLineWidth(int(self.actn3.text()))) + self.actn4.triggered.connect(lambda: self.setPlotLineWidth(int(self.actn4.text()))) + self.actn5.triggered.connect(lambda: self.setPlotLineWidth(int(self.actn5.text()))) + self.actn6.triggered.connect(lambda: self.setPlotLineWidth(int(self.actn6.text()))) + self.actn8.triggered.connect(lambda: self.setPlotLineWidth(int(self.actn8.text()))) + self.actn10.triggered.connect(lambda: self.setPlotLineWidth(int(self.actn10.text()))) + + self.actionOpen_Image_Data.triggered.connect(self.browse_file) + self.actionOpen_Multiple_Files.triggered.connect(self.createVirtualStack) + self.actionSave_as.triggered.connect(lambda: self.save_stack()) + self.actionExit.triggered.connect(lambda: QApplication.closeAllWindows()) + self.actionOpen_in_GitHub.triggered.connect(self.open_github_link) + self.actionLoad_Energy.triggered.connect(self.select_elist) + self.menuFile.setToolTipsVisible(True) + + # Accessories + self.actionOpen_Mask_Gen.triggered.connect(self.openMaskMaker) + self.actionMultiColor.triggered.connect(self.openMultiColorWindow) + + # calculations + self.pb_transpose_stack.clicked.connect(lambda: self.threadMaker(self.transposeStack)) + self.pb_swapXY_stack.clicked.connect(lambda: self.threadMaker(self.swapStackXY)) + self.pb_reset_img.clicked.connect(self.reloadImageStack) + self.pb_crop.clicked.connect(self.crop_to_dim) + self.pb_apply_crop_to_all.clicked.connect(self.apply_crop_to_all) + self.pb_crop.clicked.connect(self.view_stack) + self.sb_scaling_factor.valueChanged.connect(self.view_stack) + self.pb_ref_xanes.clicked.connect(self.select_ref_file) + self.pb_elist_xanes.clicked.connect(self.select_elist) + + # batchjobs + self.actionPlotAllCorrelations.triggered.connect(self.plotCorrelationsAllCombinations) + + [ + uis.valueChanged.connect(self.replot_image) + for uis in [self.hs_smooth_size, self.hs_nsigma, self.hs_bg_threshold] + ] + + [ + uis.stateChanged.connect(self.replot_image) + for uis in [self.cb_remove_bg, self.cb_remove_outliers, self.cb_smooth, self.cb_norm, self.cb_log] + ] + + [ + uis.stateChanged.connect(self.view_stack) + for uis in [self.cb_remove_edges, self.cb_upscale, self.cb_rebin] + ] + + # ToolBar + self.actionStack_Info.triggered.connect(self.displayStackInfo) + self.actionSave_Image.triggered.connect(self.save_disp_img) + self.actionExport_Stack.triggered.connect(lambda: self.save_stack()) + + # ROI background + self.actionSubtract_ROI_BG.triggered.connect(lambda: self.threadMaker(self.removeROIBGStack)) + + # alignment + self.pb_load_align_ref.clicked.connect(self.loadAlignRefImage) + self.pb_loadAlignTranform.clicked.connect(self.importAlignTransformation) + self.pb_saveAlignTranform.clicked.connect(self.exportAlignTransformation) + self.pb_alignStack.clicked.connect(lambda: self.threadMaker(self.stackRegistration)) + # self.pb_alignStack.clicked.connect(self.stackRegistration) + + # save_options + self.actionSave_Sum_Image.triggered.connect(lambda: self.save_stack(method="sum")) + self.actionSave_Mean_Image.triggered.connect(lambda: self.save_stack(method="mean")) + self.actionExport_Image_to_CSV.triggered.connect(self.stackToCSV) + self.pb_save_disp_spec.clicked.connect(self.save_disp_spec) + self.actionSave_Energy_List.triggered.connect(self.saveEnergyList) + self.pb_show_roi.clicked.connect(self.getROIMask) + self.pb_addToCollector.clicked.connect(self.addSpectrumToCollector) + self.pb_collect_clear.clicked.connect(lambda: self.spectrum_view_collect.clear()) + self.pb_saveCollectorPlot.clicked.connect(self.saveCollectorPlot) + + # XANES Normalization + self.pb_apply_xanes_norm.clicked.connect(self.nomalizeLiveSpec) + self.pb_auto_Eo.clicked.connect(self.findEo) + self.pb_xanes_norm_vals.clicked.connect(self.initNormVals) + self.pb_apply_norm_to_stack.clicked.connect(lambda: self.threadMaker(self.normalizeStack)) + self.actionExport_Norm_Params.triggered.connect(self.exportNormParams) + self.actionImport_Norm_Params.triggered.connect(self.importNormParams) + + # Analysis + self.pb_pca_scree.clicked.connect(self.pca_scree_) + self.pb_calc_components.clicked.connect(self.calc_comp_) + self.pb_kmeans_elbow.clicked.connect(self.kmeans_elbow) + self.pb_calc_cluster.clicked.connect(self.clustering_) + self.pb_xanes_fit.clicked.connect(self.fast_xanes_fitting) + self.pb_plot_refs.clicked.connect(self.plt_xanes_refs) + + self.show() + + self.threadpool = QThreadPool() + print(f"Multithreading with maximum {self.threadpool.maxThreadCount()} threads") + + # View Options + def darkMode(self): + self.centralwidget.setStyleSheet(open(os.path.join(ui_path, "css/darkStyle.css")).read()) + + def defaultMode(self): + self.centralwidget.setStyleSheet(open(os.path.join(ui_path, "css/defaultStyle.css")).read()) + + def modernMode(self): + self.centralwidget.setStyleSheet(open(os.path.join(ui_path, "css/modern.css")).read()) + + def setPlotLineWidth(self, width_input): + self.plotWidth = width_input + try: + self.update_spectrum() + except Exception: + pass + + def openMultiColorWindow(self): + self.multicolorwindow = MultiChannelWindow() + self.multicolorwindow.show() + + def openMaskMaker(self): + self.mask_window = MaskSpecViewer(xanes_stack=self.displayedStack, energy=self.energy) + self.mask_window.show() + + def open_github_link(self): + webbrowser.open("https://github.com/pattammattel/NSLS-II-MIDAS/wiki") + + def threadMaker(self, funct): + # Pass the function to execute + worker = Worker(funct) # Any other args, kwargs are passed to the run function + self.loadSplashScreen() + worker.signals.start.connect(self.splash.startAnimation) + worker.signals.result.connect(self.print_output) + + list( + map( + worker.signals.finished.connect, + [ + self.thread_complete, + self.splash.stopAnimation, + self.update_stack_info, + self.update_spectrum, + self.update_image_roi, + self.setImageROI + ], + ) + ) + + # Execute + self.threadpool.start(worker) + + + # File Loading + + def createVirtualStack(self): + """User can load multiple/series of tiff images with same shape. + The 'self.load_stack()' recognizes 'self.filename as list and create the stack. + """ + self.energy = [] + filter = "TIFF (*.tiff);;TIF (*.tif);;all_files (*)" + file_name = QFileDialog() + file_name.setFileMode(QFileDialog.ExistingFiles) + names = file_name.getOpenFileNames(self, "Open files", self.user_wd, filter) + if names[0]: + + self.file_name = names[0] + self.user_wd = os.path.dirname(self.file_name) + self.load_stack() + + else: + self.statusbar_main.showMessage("No file has selected") + pass + + def load_stack(self): + + """load the image data from the selected file. + If the the choice is for multiple files stack will be created in a loop. + If single h5 file is selected the unpacking will be done with 'get_xrf_data' function in StackCalcs. + From the h5 the program can recognize the beamline. The exported stack will be normalized to I0. + + If the single tiff file is choosen tf.imread() is used. + + The output 'self.im_stack' is the unmodified data file + """ + + self.log_warning = False # for the Qmessage box in cb_log + self.image_roi2_flag = False + self.cb_log.setChecked(False) + self.cb_remove_edges.setChecked(False) + self.cb_norm.setChecked(False) + self.cb_smooth.setChecked(False) + self.cb_remove_outliers.setChecked(False) + self.cb_remove_bg.setChecked(False) + self.cb_rebin.setChecked(False) + self.cb_upscale.setChecked(False) + self.sb_xrange1.setValue(0) + self.sb_yrange1.setValue(0) + self.sb_zrange1.setValue(0) + + self.menuMask.setEnabled(True) + self.actionLoad_Energy.setEnabled(True) + self.actionSave_Energy_List.setEnabled(True) + self.actionSave_as.setEnabled(True) + + self.sb_zrange2.setMaximum(99999) + self.sb_xrange2.setMaximum(99999) + self.sb_yrange2.setMaximum(99999) + + self.statusbar_main.showMessage("Loading.. please wait...") + + if isinstance(self.file_name, list): + + all_images = [] + + for im_file in self.file_name: + img = tf.imread(im_file) + all_images.append(img) # row major image + self.im_stack = np.dstack(all_images).transpose((2, 0, 1)) + self.avgIo = 1 # I0 is only applicable to XRF h5 files + self.sb_zrange2.setValue(self.im_stack.shape[0]) + + else: + + if self.file_name.endswith(".h5"): + self.im_stack, mono_e, bl_name, self.avgIo = get_xrf_data(self.file_name) + self.statusbar_main.showMessage(f"Data from {bl_name}") + self.sb_zrange2.setValue(mono_e / 10) + self.energy = [] + + elif self.file_name.endswith(".tiff") or self.file_name.endswith(".tif"): + self.im_stack_ = tf.imread(self.file_name) + if self.im_stack_.ndim == 2: + self.im_stack = self.im_stack_.reshape(1, self.im_stack_.shape[0], self.im_stack_.shape[1]) + + else: + self.im_stack = self.im_stack_ + self.sb_zrange2.setValue(self.im_stack.shape[0]) + self.autoEnergyLoader() + self.energyUnitCheck() + self.avgIo = 1 + + else: + logger.error("Unknown data format") + + """ Fill the stack dimensions to the GUI and set the image dimensions as max values. + This prevent user from choosing higher image dimensions during a resizing event""" + + logger.info(f" loaded stack with {np.shape(self.im_stack)} from the file") + + try: + logger.info(f" Transposed to shape: {np.shape(self.im_stack)}") + self.init_dimZ, self.init_dimY, self.init_dimX = self.im_stack.shape + # Remove any previously set max value during a reload + + self.sb_xrange2.setValue(self.init_dimX) + self.sb_yrange2.setValue(self.init_dimY) + + except UnboundLocalError: + logger.error("No file selected") + pass + + self.view_stack() + logger.info("Stack displayed correctly") + self.update_stack_info() + + logger.info(f"completed image shape {np.shape(self.im_stack)}") + + try: + self.statusbar_main.showMessage(f"Loaded: {self.file_name}") + + except AttributeError: + self.statusbar_main.showMessage("New Stack is made from selected tiffs") + pass + + def browse_file(self): + """To open a file widow and choose the data file. + The filename will be used to load data using 'rest and load stack' function""" + + filename = QFileDialog().getOpenFileName( + self, "Select image data", self.user_wd, "image file(*.hdf *.h5 *tiff *tif )" + ) + self.file_name = str(filename[0]) + self.user_wd = os.path.dirname(self.file_name) + + # if user decides to cancel the file window gui returns to original state + if self.file_name: + self.disconnectImageActions() + self.isAReload = False + self.load_stack() + + else: + self.statusbar_main.showMessage("No file has selected") + pass + + def autoEnergyLoader(self): + + dir_, filename_ = os.path.split(self.file_name) + self.efilePath_name = os.path.join(dir_, os.path.splitext(filename_)[0] + ".txt") + self.efilePath_log = os.path.join(dir_, "maps_log_tiff.txt") + + if os.path.isfile(self.efilePath_name): + self.efilePath = self.efilePath_name + self.efileLoader() + self.statusbar_main.showMessage(f"Energy File detected {self.efilePath}") + + elif os.path.isfile(self.efilePath_log): + self.efilePath = self.efilePath_log + self.efileLoader() + self.statusbar_main.showMessage(f"Energy File detected {self.efilePath}") + + else: + self.efilePath = False + self.efileLoader() + + def update_stack_info(self): + z, y, x = np.shape(self.displayedStack) + self.sb_zrange2.setMaximum(z + self.sb_zrange1.value()) + self.sb_xrange2.setValue(x) + self.sb_xrange2.setMaximum(x) + self.sb_yrange2.setValue(y) + self.sb_yrange2.setMaximum(y) + logger.info("Stack info has been updated") + + # Image Transformations + + def crop_to_dim(self): + self.x1, self.x2 = self.sb_xrange1.value(), self.sb_xrange2.value() + self.y1, self.y2 = self.sb_yrange1.value(), self.sb_yrange2.value() + self.z1, self.z2 = self.sb_zrange1.value(), self.sb_zrange2.value() + + try: + self.displayedStack = remove_nan_inf( + self.displayedStack[self.z1 : self.z2, self.y1 : self.y2, self.x1 : self.x2] + ) + except Exception: + self.displayedStack = remove_nan_inf( + self.im_stack[self.z1 : self.z2, self.y1 : self.y2, self.x1 : self.x2] + ) + + def apply_crop_to_all(self): + dir_ = os.path.dirname(self.file_name) + tiffs = glob(dir_+"/*.tiff") + + self.x1, self.x2 = self.sb_xrange1.value(), self.sb_xrange2.value() + self.y1, self.y2 = self.sb_yrange1.value(), self.sb_yrange2.value() + self.z1, self.z2 = self.sb_zrange1.value(), self.sb_zrange2.value() + + print(tiffs) + + for fname in tiffs: + print(fname) + im_array = tf.imread(fname) + im_name = os.path.join(dir_,os.path.basename(fname).split('.')[0]+"_cropped.tiff") + if np.ndim(im_array) == 3: + tf.imwrite(im_name, im_array[self.z1 : self.z2, self.y1 : self.y2, self.x1 : self.x2]) + logger.info(f"{im_name} saved") + elif np.ndim(im_array) == 2: + tf.imwrite(im_name, im_array[self.y1 : self.y2, self.x1 : self.x2]) + logger.info(f"{im_name} saved") + + else: + pass + + + def transpose_stack(self): + self.displayedStack = self.displayedStack.T + self.update_spectrum() + self.update_spec_image_roi() + + # Alignement + + def loadAlignRefImage(self): + filename = QFileDialog().getOpenFileName(self, "Image Data", self.user_wd, "*.tiff *.tif") + file_name = str(filename[0]) + self.user_wd = os.path.dirname(file_name) + self.alignRefImage = tf.imread(file_name) + assert self.alignRefImage.shape == self.displayedStack.shape, "Image dimensions do not match" + self.refStackAvailable = True + self.rb_alignRefVoid.setChecked(False) + self.change_color_on_load(self.pb_load_align_ref) + + def stackRegistration(self): + + self.transformations = { + "TRANSLATION": StackReg.TRANSLATION, + "RIGID_BODY": StackReg.RIGID_BODY, + "SCALED_ROTATION": StackReg.SCALED_ROTATION, + "AFFINE": StackReg.AFFINE, + "BILINEAR": StackReg.BILINEAR, + } + + self.transformType = self.transformations[self.cb_alignTransform.currentText()] + self.alignReferenceImage = self.cb_alignRef.currentText() + self.alignRefStackVoid = self.rb_alignRefVoid.isChecked() + self.alignMaxIter = self.sb_maxIterVal.value() + + if self.cb_use_tmatFile.isChecked(): + + if len(self.loaded_tranform_file) > 0: + + self.displayedStack = align_with_tmat( + self.displayedStack, tmat_file=self.loaded_tranform_file, transformation=self.transformType + ) + logger.info("Aligned to the tranform File") + + else: + logger.error("No Tranformation File Loaded") + + elif self.cb_iterAlign.isChecked(): + + if not self.refStackAvailable: + self.alignRefImage = self.displayedStack + else: + pass + + self.displayedStack = align_stack_iter( + self.displayedStack, + ref_stack_void=False, + ref_stack=self.alignRefImage, + transformation=self.transformType, + method=("previous", "first"), + max_iter=self.alignMaxIter, + ) + + else: + if not self.refStackAvailable: + self.alignRefImage = self.displayedStack + + else: + pass + + self.displayedStack, self.tranform_file = align_stack( + self.displayedStack, + ref_image_void=True, + ref_stack=self.alignRefImage, + transformation=self.transformType, + reference=self.alignReferenceImage, + ) + logger.info("New Tranformation file available") + self.im_stack = self.displayedStack + + def exportAlignTransformation(self): + + + + file_name = QFileDialog().getSaveFileName( + self, + "Save Transformation File", + os.path.join(self.user_wd,"TranformationMatrix.npy"), + "text file (*.npy)" + ) + if file_name[0]: + np.save(file_name[0], self.tranform_file) + self.user_wd = os.path.dirname(file_name[0]) + else: + pass + + def importAlignTransformation(self): + file_name = QFileDialog().getOpenFileName(self, "Open Transformation File", self.user_wd, "text file (*.npy)") + if file_name[0]: + self.loaded_tranform_file = np.load(file_name[0]) + self.cb_use_tmatFile.setChecked(True) + self.user_wd = os.path.dirname(file_name[0]) + logger.info("Transformation File Loaded") + else: + pass + + def loadSplashScreen(self): + self.splash = LoadingScreen() + + px = self.geometry().x() + py = self.geometry().y() + ph = self.geometry().height() + pw = self.geometry().width() + dw = self.splash.width() + dh = self.splash.height() + new_x, new_y = px + (0.5 * pw) - dw, py + (0.5 * ph) - dh + self.splash.setGeometry(int(new_x), int(new_y), int(dw), int(dh)) + + self.splash.show() + + def reloadImageStack(self): + self.isAReload = True + self.load_stack() + + def update_stack(self): + self.displayedStack = self.im_stack + self.crop_to_dim() + + if self.cb_rebin.isChecked(): + self.cb_upscale.setChecked(False) + self.sb_scaling_factor.setEnabled(True) + self.displayedStack = resize_stack(self.displayedStack, scaling_factor=self.sb_scaling_factor.value()) + self.update_stack_info() + + elif self.cb_upscale.isChecked(): + self.cb_rebin.setChecked(False) + self.sb_scaling_factor.setEnabled(True) + self.displayedStack = resize_stack( + self.displayedStack, upscaling=True, scaling_factor=self.sb_scaling_factor.value() + ) + self.update_stack_info() + + if self.cb_remove_outliers.isChecked(): + self.hs_nsigma.setEnabled(True) + nsigma = self.hs_nsigma.value() / 10 + self.displayedStack = remove_hot_pixels(self.displayedStack, NSigma=nsigma) + self.label_nsigma.setText(str(nsigma)) + logger.info(f"Removing Outliers with NSigma {nsigma}") + + elif self.cb_remove_outliers.isChecked() is False: + self.hs_nsigma.setEnabled(False) + + if self.cb_remove_edges.isChecked(): + self.displayedStack = remove_edges(self.displayedStack) + logger.info(f"Removed edges, new shape {self.displayedStack.shape}") + self.update_stack_info() + + if self.cb_remove_bg.isChecked(): + self.hs_bg_threshold.setEnabled(True) + logger.info("Removing background") + bg_threshold = self.hs_bg_threshold.value() + self.label_bg_threshold.setText(str(bg_threshold) + "%") + self.displayedStack = clean_stack(self.displayedStack, auto_bg=False, bg_percentage=bg_threshold) + + elif self.cb_remove_bg.isChecked() is False: + self.hs_bg_threshold.setEnabled(False) + + if self.cb_log.isChecked(): + + self.displayedStack = remove_nan_inf(np.log10(self.displayedStack)) + logger.info("Log Stack is in use") + + if self.cb_smooth.isChecked(): + self.hs_smooth_size.setEnabled(True) + window = self.hs_smooth_size.value() + if window % 2 == 0: + window = +1 + self.smooth_winow_size.setText("Window size: " + str(window)) + self.displayedStack = smoothen(self.displayedStack, w_size=window) + logger.info("Spectrum Smoothening Applied") + + elif self.cb_smooth.isChecked() is False: + self.hs_smooth_size.setEnabled(False) + + if self.cb_norm.isChecked(): + logger.info("Normalizing spectra") + self.displayedStack = normalize(self.displayedStack, norm_point=-1) + + logger.info("Updated image is in use") + + # ImageView + + def view_stack(self): + + if not self.im_stack.ndim == 3: + raise ValueError("stack should be an ndarray with ndim == 3") + else: + self.update_stack() + # self.StackUpdateThread() + + try: + self.image_view.removeItem(self.image_roi_math) + except Exception: + pass + + (self.dim1, self.dim2, self.dim3) = self.displayedStack.shape + self.image_view.setImage(self.displayedStack) + self.image_view.ui.menuBtn.hide() + self.image_view.ui.roiBtn.hide() + self.image_view.setPredefinedGradient("viridis") + self.image_view.setCurrentIndex(self.dim1 // 2) + if len(self.energy) == 0: + self.energy = np.arange(self.z1, self.z2) * 10 + logger.info("Arbitary X-axis used in the plot for XANES") + self.sz = np.max( + [int(self.dim2 * 0.1), int(self.dim3 * 0.1)] + ) # size of the roi set to be 10% of the image area + + self.stack_center = self.energy[len(self.energy) // 2] + self.stack_width = (self.energy.max() - self.energy.min()) // 10 + self.spec_roi = pg.LinearRegionItem( + values=(self.stack_center - self.stack_width, self.stack_center + self.stack_width) + ) + + # a second optional ROI for calculations follow + self.image_roi_math = pg.PolyLineROI( + [[0, 0], [0, self.sz], [self.sz, self.sz], [self.sz, 0]], + pos=(int(self.dim3 // 3), int(self.dim2 // 3)), + pen="r", + closed=True, + removable=True, + ) + + self.spec_roi_math = pg.LinearRegionItem( + values=(self.stack_center - self.stack_width - 10, self.stack_center + self.stack_width - 10), + pen="r", + brush=QtGui.QColor(0, 255, 200, 50), + ) + self.spec_lo_m_idx = self.spec_hi_m_idx = 0 + + self.setImageROI() + self.update_spectrum() + self.update_image_roi() + + if not self.isAReload: + # image connections + self.image_view.mousePressEvent = self.getPointSpectrum + self.pb_apply_spec_calc.clicked.connect(self.spec_roi_calc) + self.rb_math_roi.clicked.connect(self.update_spectrum) + self.pb_add_roi_2.clicked.connect(self.math_img_roi_flag) + self.image_roi_math.sigRegionChangeFinished.connect(self.image_roi_calc) + self.pb_apply_img_calc.clicked.connect(self.image_roi_calc) + + self.spec_roi.sigRegionChanged.connect(self.update_image_roi) + self.spec_roi_math.sigRegionChangeFinished.connect(self.spec_roi_calc) + + [ + rbs.clicked.connect(self.setImageROI) + for rbs in [self.rb_poly_roi, self.rb_elli_roi, self.rb_rect_roi, self.rb_line_roi, self.rb_circle_roi] + ] + + def disconnectImageActions(self): + for btns in [self.pb_apply_spec_calc, self.rb_math_roi, self.pb_add_roi_2, self.pb_apply_img_calc]: + try: + btns.disconnect() + except Exception: + pass + + def select_elist(self): + self.energyFileChooser() + self.efileLoader() + self.energyUnitCheck() + self.view_stack() + + def efileLoader(self): + + if self.efilePath: + + if str(self.efilePath).endswith("log_tiff.txt"): + self.energy = energy_from_logfile(logfile=str(self.efilePath)) + logger.info("Log file from pyxrf processing") + + else: + self.energy = np.loadtxt(str(self.efilePath)) + self.change_color_on_load(self.pb_elist_xanes) + logger.info("Energy file loaded") + + else: + self.statusbar_main.showMessage("No Energy List Selected, Setting Arbitary Axis") + self.energy = np.arange(self.im_stack.shape[0]) + logger.info("Arbitary Energy Axis") + + # assert len(self.energy) == self.dim1, "Number of Energy Points is not equal to stack length" + + def energyUnitCheck(self): + + if np.max(self.energy) < 100: + self.cb_kev_flag.setChecked(True) + self.energy *= 1000 + + else: + self.cb_kev_flag.setChecked(False) + + def select_ref_file(self): + self.pb_xanes_fit.setEnabled(True) + self.ref_names = [] + file_name = QFileDialog().getOpenFileName(self, "Open reference file", self.user_wd, "text file (*.csv *.nor)") + if file_name[0]: + if file_name[0].endswith(".nor"): + self.refs, self.ref_names = create_df_from_nor_try2(athenafile=str(file_name[0])) + self.change_color_on_load(self.pb_ref_xanes) + + elif file_name[0].endswith(".csv"): + self.refs = pd.read_csv(str(file_name[0])) + self.ref_names = list(self.refs.keys()) + + self.change_color_on_load(self.pb_ref_xanes) + + self.user_wd = os.path.dirname(file_name[0]) + + else: + logger.error("No file selected") + pass + + logger.info(f"{self.refs.shape = }") + + self.plt_xanes_refs() + + def plt_xanes_refs(self): + + try: + self.ref_plot.close() + except Exception: + pass + + self.ref_plot = plot(title="Reference Standards") + self.ref_plot.setLabel("bottom", "Energy") + self.ref_plot.setLabel("left", "Intensity") + self.ref_plot.addLegend() + + for n in range(np.shape(self.refs)[1]): + + if not n == 0: + self.ref_plot.plot( + self.refs.values[:, 0], + self.refs.values[:, n], + pen=pg.mkPen(self.plt_colors[n - 1], width=self.plotWidth), + name=self.ref_names[n], + ) + + def getPointSpectrum(self, event): + if event.type() == QtCore.QEvent.MouseButtonDblClick: + if event.button() == QtCore.Qt.LeftButton: + self.xpixel = int(self.image_view.view.mapSceneToView(event.pos()).x()) - 1 + zlim, ylim, xlim = self.displayedStack.shape + + if self.xpixel > xlim: + self.xpixel = xlim - 1 + + self.ypixel = int(self.image_view.view.mapSceneToView(event.pos()).y()) - 1 + if self.ypixel > ylim: + self.ypixel = ylim - 1 + self.spectrum_view.addLegend() + self.point_spectrum = self.displayedStack[:, self.ypixel, self.xpixel] + self.spectrum_view.plot( + self.xdata, + self.point_spectrum, + clear=True, + pen=pg.mkPen(pg.mkColor(0, 0, 255, 255), width=self.plotWidth), + symbol="o", + symbolSize=6, + symbolBrush="r", + name=f"Point Spectrum; x= {self.xpixel}, y= {self.ypixel}", + ) + + self.spectrum_view.addItem(self.spec_roi) + + self.statusbar_main.showMessage(f"{self.xpixel} and {self.ypixel}") + + def setImageROI(self): + + self.lineROI = pg.LineSegmentROI([[int(self.dim3 // 2), int(self.dim2 // 2)], [self.sz, self.sz]], pen="r") + + self.rectROI = pg.RectROI( + [int(self.dim3 // 2), int(self.dim2 // 2)], + [self.sz, self.sz], + pen="w", + maxBounds=QtCore.QRectF(0, 0, self.dim3, self.dim2), + ) + + self.rectROI.addTranslateHandle([0, 0], [2, 2]) + self.rectROI.addRotateHandle([0, 1], [2, 2]) + + self.ellipseROI = pg.EllipseROI( + [int(self.dim3 // 2), int(self.dim2 // 2)], + [self.sz, self.sz], + pen="w", + maxBounds=QtCore.QRectF(0, 0, self.dim3, self.dim2), + ) + + self.circleROI = pg.CircleROI( + [int(self.dim3 // 2), int(self.dim2 // 2)], + [self.sz, self.sz], + pen="w", + maxBounds=QtCore.QRectF(0, 0, self.dim3, self.dim2), + ) # pos and size + + self.polyLineROI = pg.PolyLineROI( + [[0, 0], [0, self.sz], [self.sz, self.sz], [self.sz, 0]], + pos=(int(self.dim3 // 2), int(self.dim2 // 2)), + maxBounds=QtCore.QRectF(0, 0, self.dim3, self.dim2), + closed=True, + removable=True, + ) + + self.rois = { + "rb_line_roi": self.lineROI, + "rb_rect_roi": self.rectROI, + "rb_circle_roi": self.circleROI, + "rb_elli_roi": self.ellipseROI, + "rb_poly_roi": self.polyLineROI, + } + + button_name = self.sender() + + if button_name.objectName() in self.rois.keys(): + self.roi_preference = button_name.objectName() + + else: + self.roi_preference = "rb_rect_roi" # default + + try: + self.image_view.removeItem(self.image_roi) + + except Exception: + pass + + # ROI settings for image, used polyline roi with non rectangular shape + + self.image_roi = self.rois[self.roi_preference] + self.image_view.addItem(self.image_roi) + self.image_roi.sigRegionChanged.connect(self.update_spectrum) + + def replot_image(self): + self.update_stack() + self.update_spectrum() + self.update_image_roi() + + def update_spec_roi_values(self): + self.stack_center = int(self.energy[len(self.energy) // 2]) + self.stack_width = int((self.energy.max() - self.energy.min()) * 0.05) + self.spec_roi.setBounds([self.xdata[0], self.xdata[-1]]) # if want to set bounds for the spec roi + self.spec_roi_math.setBounds([self.xdata[0], self.xdata[-1]]) + + def update_spectrum(self): + + # set x-axis values; array taken from energy values, if clipped z box values will update the array + self.xdata = self.energy[self.sb_zrange1.value() : self.sb_zrange2.value()] + + # get the cropped stack from ROI region; pyqtgraph function is used + self.roi_img_stk = self.image_roi.getArrayRegion( + self.displayedStack, self.image_view.imageItem, axes=(1, 2) + ) + + posx, posy = self.image_roi.pos() + self.le_roi.setText(str(int(posx)) + ":" + str(int(posy))) + + # display the ROI features in the line edit boxes + if self.roi_img_stk.ndim == 3: + sizex, sizey = self.roi_img_stk.shape[1], self.roi_img_stk.shape[2] + self.le_roi_size.setText(str(sizex) + "," + str(sizey)) + self.mean_spectra = get_mean_spectra(self.roi_img_stk) + + elif self.roi_img_stk.ndim == 2: + sizex, sizey = self.roi_img_stk.shape[0], self.roi_img_stk.shape[1] + self.le_roi_size.setText(str(sizex) + "," + str(sizey)) + self.mean_spectra = self.roi_img_stk.mean(-1) + + self.spectrum_view.addLegend() + + try: + self.spectrum_view.plot( + self.xdata, + self.mean_spectra, + pen=pg.mkPen(pg.mkColor(5, 255, 5, 255), width=self.plotWidth), + clear=True, + symbol="o", + symbolSize=6, + symbolBrush="r", + name="ROI Spectrum", + ) + except Exception: + self.spectrum_view.plot( + self.mean_spectra, + clear=True, + pen=pg.mkPen(pg.mkColor(5, 255, 5, 255), width=self.plotWidth), + symbol="o", + symbolSize=6, + symbolBrush="r", + name="ROI Spectrum", + ) + + if self.energy[-1] > 1000: + self.e_unit = "eV" + else: + self.e_unit = "keV" + + self.spectrum_view.setLabel("bottom", "Energy", self.e_unit) + self.spectrum_view.setLabel("left", "Intensity", "A.U.") + self.spectrum_view.addItem(self.spec_roi) + self.update_spec_roi_values() + self.math_roi_flag() + + def update_image_roi(self): + self.spec_lo, self.spec_hi = self.spec_roi.getRegion() + self.spec_lo_idx = (np.abs(self.energy - self.spec_lo)).argmin() + self.spec_hi_idx = (np.abs(self.energy - self.spec_hi)).argmin() + self.le_spec_roi.setText(str(int(self.spec_lo)) + ":" + str(int(self.spec_hi))) + self.le_spec_roi_size.setText(str(int(self.spec_hi - self.spec_lo))) + self.update_spec_roi_values() + self.stackIndexToNames() + + try: + if int(self.spec_lo_idx) == int(self.spec_hi_idx): + self.disp_img = self.displayedStack[int(self.spec_hi_idx), :, :] + + else: + self.disp_img = self.displayedStack[int(self.spec_lo_idx) : int(self.spec_hi_idx), :, :].mean(0) + + self.image_view.setImage(self.disp_img) + self.statusbar_main.showMessage(f"Image Display is {self.corrImg1}") + except Exception: + logger.warning("Indices are out of range; Image cannot be created") + pass + + def set_spec_roi(self): + self.spec_lo_, self.spec_hi_ = int(self.sb_roi_spec_s.value()), int(self.sb_roi_spec_e.value()) + self.spec_lo_idx_ = (np.abs(self.energy - self.spec_lo_)).argmin() + self.spec_hi_idx_ = (np.abs(self.energy - self.spec_hi_)).argmin() + self.spec_roi.setRegion((self.xdata[self.spec_lo_idx_], self.xdata[self.spec_hi_idx_])) + self.update_image_roi() + + def math_roi_flag(self): + if self.rb_math_roi.isChecked(): + self.rb_math_roi.setStyleSheet("color : green") + self.spectrum_view.addItem(self.spec_roi_math) + else: + self.spectrum_view.removeItem(self.spec_roi_math) + + def spec_roi_calc(self): + + self.spec_lo_m, self.spec_hi_m = self.spec_roi_math.getRegion() + self.spec_lo_m_idx = (np.abs(self.energy - self.spec_lo_m)).argmin() + self.spec_hi_m_idx = (np.abs(self.energy - self.spec_hi_m)).argmin() + + if int(self.spec_lo_idx) == int(self.spec_hi_idx): + self.img1 = self.displayedStack[int(self.spec_hi_idx), :, :] + + else: + self.img1 = self.displayedStack[int(self.spec_lo_idx) : int(self.spec_hi_idx), :, :].mean(0) + + if int(self.spec_lo_m_idx) == int(self.spec_hi_m_idx): + self.img2 = self.displayedStack[int(self.spec_hi_m_idx), :, :] + + else: + self.img2 = self.displayedStack[int(self.spec_lo_m_idx) : int(self.spec_hi_m_idx), :, :].mean(0) + + if self.cb_roi_operation.currentText() == "Correlation Plot": + self.correlation_plot() + + else: + calc = {"Divide": np.divide, "Subtract": np.subtract, "Add": np.add} + self.disp_img = remove_nan_inf(calc[self.cb_roi_operation.currentText()](self.img1, self.img2)) + self.image_view.setImage(self.disp_img) + + def math_img_roi_flag(self): + + button_name = self.sender().text() + logger.info(f"{button_name}") + if button_name == "Add ROI_2": + self.image_view.addItem(self.image_roi_math) + self.pb_add_roi_2.setText("Remove ROI_2") + self.image_roi2_flag = 1 + elif button_name == "Remove ROI_2": + self.image_view.removeItem(self.image_roi_math) + self.pb_add_roi_2.setText("Add ROI_2") + self.image_roi2_flag = 0 + + else: + pass + logger.error("Unknown signal for second ROI") + + def image_roi_calc(self): + + if self.image_roi2_flag == 1: + self.calc = {"Divide": np.divide, "Subtract": np.subtract, "Add": np.add} + self.update_spec_image_roi() + else: + logger.error("No ROI2 found") + return + + def update_spec_image_roi(self): + + self.math_roi_reg = self.image_roi_math.getArrayRegion( + self.displayedStack, self.image_view.imageItem, axes=(1, 2) + ) + if self.math_roi_reg.ndim == 3: + + self.math_roi_spectra = get_mean_spectra(self.math_roi_reg) + + elif self.roi_img_stk.ndim == 2: + self.math_roi_spectra = self.math_roi_reg.mean(-1) + + if self.cb_img_roi_action.currentText() in self.calc.keys(): + + calc_spec = self.calc[self.cb_img_roi_action.currentText()](self.mean_spectra, self.math_roi_spectra) + self.spectrum_view.addLegend() + self.spectrum_view.plot( + self.xdata, + calc_spec, + clear=True, + pen=pg.mkPen("m", width=2), + name=self.cb_img_roi_action.currentText() + "ed", + ) + self.spectrum_view.plot(self.xdata, self.math_roi_spectra, pen=pg.mkPen("y", width=2), name="ROI2") + self.spectrum_view.plot(self.xdata, self.mean_spectra, pen=pg.mkPen("g", width=2), name="ROI1") + + elif self.cb_img_roi_action.currentText() == "Compare": + self.spectrum_view.plot( + self.xdata, self.math_roi_spectra, pen=pg.mkPen("y", width=2), clear=True, name="ROI2" + ) + self.spectrum_view.plot(self.xdata, self.mean_spectra, pen=pg.mkPen("g", width=2), name="ROI1") + + self.spectrum_view.addItem(self.spec_roi) + + def displayStackInfo(self): + + try: + + if isinstance(self.file_name, list): + info = f"Folder; {os.path.dirname(self.file_name[0])} \n" + for n, name in enumerate(self.file_name): + info += f"{n}: {os.path.basename(name)} \n" + + # info = f'Stack order; {[name for name in enumerate(self.file_name)]}' + else: + info = f"Stack; {self.file_name}" + + self.infoWindow = StackInfo(str(info)) + self.infoWindow.show() + + except AttributeError: + self.statusbar_main.showMessage("Warning: No Image Data Loaded") + + def stackIndexToNames(self): + # create list of tiff file names for virtutal stack for plot axes + self.elemFileName = [] + + if isinstance(self.file_name, list): + for name in self.file_name: + self.elemFileName.append(os.path.basename(name).split(".")[0]) + + logger.info(f" Virtual Stack - list of image names; {self.elemFileName}") + + # if the roi focus on one frame, Note that this slicing excludes the last index + if int(self.spec_lo_idx) == int(self.spec_hi_idx): + self.corrImg1 = str(self.elemFileName[int(self.spec_lo_idx)]) + else: + self.corrImg1 = self.elemFileName[int(self.spec_lo_idx) : int(self.spec_hi_idx)] + if len(self.corrImg1) > 1: + self.corrImg1 = f"Sum of {self.corrImg1} " + + if int(self.spec_lo_m_idx) == int(self.spec_hi_m_idx): + self.corrImg2 = str(self.elemFileName[int(self.spec_lo_m_idx)]) + + else: + self.corrImg2 = self.elemFileName[int(self.spec_lo_m_idx) : int(self.spec_hi_m_idx)] + + if len(self.corrImg2) > 1: + self.corrImg2 = f"Sum of {self.corrImg2}" + + logger.info( + f"Correlation stack {int(self.spec_lo_idx)}:{int(self.spec_hi_idx)} with " + f"{int(self.spec_lo_m_idx)}:{int(self.spec_hi_m_idx)}" + ) + + logger.info(f" Virtual Stack; corrlation plot of {self.corrImg1} vs {self.corrImg2}") + else: + self.corrImg1 = ( + f" Sum of {os.path.basename(self.file_name).split('.')[0]}_{int(self.spec_lo_idx)} " + f"to {int(self.spec_hi_idx)}" + ) + self.corrImg2 = ( + f" Sum of {os.path.basename(self.file_name).split('.')[0]}_{int(self.spec_lo_m_idx)} " + f"to {int(self.spec_hi_m_idx)}" + ) + # logger.info(f" corrlation plot of {self.corrImg1} vs {self.corrImg2}") + + def stackToCSV(self): + + self.stackIndexToNames() + self.imageDf = pd.DataFrame() + if len(self.elemFileName) == len(self.displayedStack): + for name, image in zip(self.elemFileName, self.displayedStack): + self.imageDf[f'{name}'] = image.flatten() + # print(self.imageDf.head()) + else: + self.imageDf = image_to_pandas2(self.displayedStack) + + file_name = QFileDialog().getSaveFileName(self, + "Save CSV Data", + os.path.join(self.user_wd,'image_2DArray.csv'), + 'file (*csv)') + if file_name[0]: + self.imageDf.to_csv(path_or_buf=file_name[0]) + self.user_wd = os.path.dirname(file_name[0]) + self.statusbar_main.showMessage(f"Data saved to {str(file_name[0])}") + else: + pass + + def correlation_plot(self): + self.stackIndexToNames() + + self.statusbar_main.showMessage(f"Correlation of {self.corrImg1} with {self.corrImg2}") + + if self.rb_roiRegionOnly.isChecked(): + self.roi_mask = self.image_roi.getArrayRegion( + self.displayedStack, self.image_view.imageItem, axes=(1, 2) + ) + self.roi_img1 = np.mean(self.roi_mask[int(self.spec_lo_idx) : int(self.spec_hi_idx)], axis=0) + self.roi_img2 = np.mean(self.roi_mask[int(self.spec_lo_m_idx) : int(self.spec_hi_m_idx)], axis=0) + self.scatter_window = ScatterPlot( + self.roi_img1, self.roi_img2, (str(self.corrImg1), str(self.corrImg2)) + ) + + else: + + self.scatter_window = ScatterPlot(self.img1, self.img2, (str(self.corrImg1), str(self.corrImg2))) + + self.scatter_window.show() + + def plotCorrelationsAllCombinations(self): + + print("Plotting all correlations") + self.stackIndexToNames() + allElemCombNum = list(combinations(np.arange(len(self.elemFileName)), 2)) + + self.scW1 = self.scW2 = self.scW3 = self.scW4 = self.scW5 = None + self.scW6 = self.scW7 = self.scW8 = self.scW9 = self.scW10 = None + + self.scWindowList = [ + self.scW1, + self.scW2, + self.scW3, + self.scW4, + self.scW5, + self.scW6, + self.scW7, + self.scW8, + self.scW9, + self.scW10, + ] + self.scWindowDict = { + 1: self.scW1, + 2: self.scW2, + 3: self.scW3, + 4: self.scW4, + 5: self.scW5, + 6: self.scW6, + 7: self.scW7, + 8: self.scW8, + 9: self.scW9, + 10: self.scW10, + } + + if len(allElemCombNum) > len(self.scWindowDict): + + reply = QMessageBox.warning( + self, + "Plot Window Limit", + f"The number of combination exceeds " + f"maxiumum number of " + f"plot windows. First {len(self.scWindowDict)} " + f"combinations will be plotted. \n Proceed?", + QMessageBox.Yes | QMessageBox.No, + QMessageBox.No, + ) + + if reply == QMessageBox.Yes: + + for i, pair in enumerate(allElemCombNum): + im1 = self.displayedStack[pair[0]] + im2 = self.displayedStack[pair[1]] + im1Name = self.elemFileName[pair[0]] + im2Name = self.elemFileName[pair[1]] + + self.scWindowDict[i] = ScatterPlot(im1, im2, (str(im1Name), str(im2Name))) + self.scWindowDict[i].show() + + if reply == QMessageBox.No: + return + + else: + + for i, pair in enumerate(allElemCombNum): + im1 = self.displayedStack[pair[0]] + im2 = self.displayedStack[pair[1]] + im1Name = self.elemFileName[pair[0]] + im2Name = self.elemFileName[pair[1]] + + self.scWindowDict[i] = ScatterPlot(im1, im2, (str(im1Name), str(im2Name))) + self.scWindowDict[i].show() + + def getROIMask(self): + self.roi_mask = self.image_roi.getArrayRegion(self.displayedStack, self.image_view.imageItem, axes=(1, 2)) + self.newWindow = singleStackViewer(self.roi_mask) + self.newWindow.show() + + def save_stack(self, method="raw"): + + # self.update_stack() + file_name = QFileDialog().getSaveFileName( + self, + "Save image data", + os.path.join(self.user_wd,"image_data.tiff"), + "image file(*tiff *tif )" + ) + if file_name[0]: + if method == "raw": + + tf.imsave(str(file_name[0]), self.displayedStack) + logger.info(f"Updated Image Saved: {str(file_name[0])}") + self.statusbar_main.showMessage(f"Updated Image Saved: {str(file_name[0])}") + elif method == "sum": + tf.imsave(str(file_name[0]), np.sum(self.displayedStack, axis=0)) + + elif method == "mean": + tf.imsave(str(file_name[0]), np.mean(self.displayedStack, axis=0)) + + self.user_wd = os.path.dirname(file_name[0]) + + else: + self.statusbar_main.showMessage("Saving cancelled") + logger.info(f"Save failed: {str(file_name[0])}") + pass + + def save_disp_img(self): + file_name = QFileDialog().getSaveFileName(self, + "Save image data", + os.path.join(self.user_wd,"image.tiff"), + "image file(*tiff *tif )") + if file_name[0]: + tf.imsave(str(file_name[0]), self.disp_img) + self.statusbar_main.showMessage(f"Image Saved to {str(file_name[0])}") + self.user_wd = os.path.dirname(file_name[0]) + logger.info(f"Updated Image Saved: {str(file_name[0])}") + + else: + logger.error("No file to save") + self.statusbar_main.showMessage("Saving cancelled") + pass + + def getLivePlotData(self): + try: + + data = np.squeeze([c.getData() for c in self.spectrum_view.plotItem.curves]) + # print(np.shape(data)) + if data.ndim == 2: + self.mu_ = data[1] + self.e_ = data[0] + elif data.ndim == 3: + e_mu = data[0, :, :] + self.mu_ = e_mu[1] + self.e_ = e_mu[0] + + else: + logger.error(f" Data shape of {data.ndim} is not supported") + pass + except AttributeError: + logger.error("No data loaded") + pass + + def addSpectrumToCollector(self): + self.getLivePlotData() + self.spectrum_view_collect.plot(self.e_, self.mu_, name="ROI Spectrum") + self.spectrum_view_collect.setLabel("bottom", "Energy", self.e_unit) + self.spectrum_view_collect.setLabel("left", "Intensity", "A.U.") + + def findEo(self): + try: + self.getLivePlotData() + e0_init = self.e_[np.argmax(np.gradient(self.mu_))] + self.dsb_norm_Eo.setValue(e0_init) + + except AttributeError: + logger.error("No data loaded") + pass + + def initNormVals(self): + self.getLivePlotData() + e0_init = self.e_[np.argmax(np.gradient(self.mu_))] + pre1, pre2, post1, post2 = xanesNormalization( + self.e_, + self.mu_, + e0=e0_init, + step=None, + nnorm=1, + nvict=0, + method= "guess" + ) + self.dsb_norm_pre1.setValue(pre1) + self.dsb_norm_pre2.setValue(pre2) + self.dsb_norm_post1.setValue(post1) + self.dsb_norm_post2.setValue(post2) + self.dsb_norm_Eo.setValue(e0_init) + + def getNormParams(self): + self.getLivePlotData() + eo_ = self.dsb_norm_Eo.value() + pre1_, pre2_ = self.dsb_norm_pre1.value(), self.dsb_norm_pre2.value() + norm1_, norm2_ = self.dsb_norm_post1.value(), self.dsb_norm_post2.value() + norm_order = self.sb_norm_order.value() + + return eo_, pre1_, pre2_, norm1_, norm2_, norm_order + + def exportNormParams(self): + self.xanesNormParam = {} + eo_, pre1_, pre2_, norm1_, norm2_, norm_order = self.getNormParams() + self.xanesNormParam["E0"] = eo_ + self.xanesNormParam["pre1"] = pre1_ + self.xanesNormParam["pre2"] = pre2_ + self.xanesNormParam["post1"] = norm1_ + self.xanesNormParam["post2"] = norm2_ + self.xanesNormParam["norm_order"] = norm_order + + file_name = QtWidgets.QFileDialog().getSaveFileName( + self, + "Save XANES Norm Params", + os.path.join(self.user_wd,"xanes_norm_params.csv"), + "csv file(*csv)" + ) + if file_name[0]: + pd.DataFrame(self.xanesNormParam, index=[0]).to_csv(file_name[0]) + self.user_wd = os.path.dirname(file_name[0]) + + else: + pass + + def importNormParams(self): + + file_name = QtWidgets.QFileDialog().getOpenFileName( + self, "Open a XANES Norm File", self.user_wd, "csv file(*csv);;all_files (*)" + ) + + if file_name[0]: + xanesNormParam = pd.read_csv(file_name[0]) + self.dsb_norm_Eo.setValue(xanesNormParam["E0"]) + self.dsb_norm_pre1.setValue(xanesNormParam["pre1"]) + self.dsb_norm_pre2.setValue(xanesNormParam["pre2"]) + self.dsb_norm_post1.setValue(xanesNormParam["post1"]) + self.dsb_norm_post2.setValue(xanesNormParam["post2"]) + self.sb_norm_order.setValue(xanesNormParam["norm_order"]) + self.user_wd = os.path.dirname(file_name[0]) + + def nomalizeLiveSpec(self): + eo_, pre1_, pre2_, norm1_, norm2_, norm_order = self.getNormParams() + self.spectrum_view.clear() + colors = np.array(("c", "r", "m")) + + if self.cb_mback.isChecked(): + f2, normXANES = xanesNormalization( + self.e_, + self.mu_, + e0=eo_, + step=None, + nnorm=norm_order, + nvict=0, + pre1=pre1_, + pre2=pre2_, + norm1=norm1_, + norm2=norm2_, + useFlattened=self.cb_xanes_flat.isChecked(), + method = "mback" + ) + + names = np.array(("matched mu(E)", "f2")) + data_array = np.array((normXANES, f2)) + + else: + pre_line, post_line, normXANES = xanesNormalization( + self.e_, + self.mu_, + e0=eo_, + step=None, + nnorm=norm_order, + nvict=0, + pre1=pre1_, + pre2=pre2_, + norm1=norm1_, + norm2=norm2_, + useFlattened=self.cb_xanes_flat.isChecked() + ) + + names = np.array(("Spectrum", "Pre", "Post")) + data_array = np.array((self.mu_, pre_line, post_line)) + + + for data, clr, name in zip(data_array, colors, names): + self.spectrum_view.plot(self.e_, data, pen=pg.mkPen(clr, width=self.plotWidth), name=name) + + self.spectrum_view_norm.plot( + self.e_, normXANES, clear=True, pen=pg.mkPen(self.plt_colors[-1], width=self.plotWidth)) + + self.spectrum_view_norm.setLabel("bottom", "Energy", self.e_unit) + self.spectrum_view_norm.setLabel("left", "Norm. Intensity", "A.U.") + + def normalizeStack(self): + self.getLivePlotData() + eo_, pre1_, pre2_, norm1_, norm2_, norm_order = self.getNormParams() + + self.im_stack = self.displayedStack = xanesNormStack( + self.e_, + self.displayedStack, + e0=eo_, + step=None, + nnorm=norm_order, + nvict=0, + pre1=pre1_, + pre2=pre2_, + norm1=norm1_, + norm2=norm2_, + useFlattened=self.cb_xanes_flat.isChecked(), + ignorePostEdgeNorm=self.cb_xanes_postedge.isChecked() + ) + # self.im_stack = self.displayedStack + + def transposeStack(self): + self.im_stack = self.displayedStack = np.transpose(self.displayedStack, (2, 1, 0)) + + def swapStackXY(self): + self.im_stack = self.displayedStack = np.transpose(self.displayedStack, (0, 2, 1)) + + def removeROIBGStack(self): + self.displayedStack = subtractBackground(self.displayedStack, self.mean_spectra) + + def resetCollectorSpec(self): + pass + + def saveCollectorPlot(self): + exporter = pg.exporters.CSVExporter(self.spectrum_view_collect.plotItem) + exporter.parameters()["columnMode"] = "(x,y,y,y) for all plots" + file_name = QFileDialog().getSaveFileName(self, "save spectra", self.user_wd, "spectra (*csv)") + if file_name[0]: + exporter.export(str(file_name[0]) + ".csv") + self.user_wd = os.path.dirname(file_name[0]) + else: + self.statusbar_main.showMessage("Saving cancelled") + pass + + def save_disp_spec(self): + + exporter = pg.exporters.CSVExporter(self.spectrum_view.plotItem) + exporter.parameters()["columnMode"] = "(x,y,y,y) for all plots" + file_name = QFileDialog().getSaveFileName(self, + "save spectrum", + os.path.join(self.user_wd,"spectrum.csv"), + "spectra (*csv)") + if file_name[0]: + exporter.export(str(file_name[0]) + ".csv") + self.user_wd = os.path.dirname(file_name[0]) + else: + self.statusbar_main.showMessage("Saving cancelled") + pass + + def saveEnergyList(self): + file_name = QFileDialog().getSaveFileName(self, + "save energy list", + os.path.join(self.user_wd,"energy_list.txt"), + "text file (*txt)") + if file_name[0]: + np.savetxt(file_name[0], self.xdata, fmt="%.4f") + self.user_wd = os.path.dirname(file_name[0]) + else: + pass + + def pca_scree_(self): + logger.info("Process started..") + self.update_stack() + var = pca_scree(self.displayedStack) + + pca_scree_plot = pg.plot( + var[:24], title="Scree Plot", pen=pg.mkPen("y", width=2, style=QtCore.Qt.DotLine), symbol="o" + ) + pca_scree_plot.addLine(y=0) + pca_scree_plot.setLabel("bottom", "Component Number") + pca_scree_plot.setLabel("left", "Singular Values") + + logger.info("Process complete") + + def calc_comp_(self): + + logger.info("Process started..") + + # self.update_stack() + n_components = self.sb_ncomp.value() + method_ = self.cb_comp_method.currentText() + + ims, comp_spec, decon_spec, decomp_map = decompose_stack( + self.displayedStack, decompose_method=method_, n_components_=n_components + ) + + self._new_window3 = ComponentViewer(ims, self.xdata, comp_spec, decon_spec, decomp_map) + self._new_window3.show() + + logger.info("Process complete") + + def kmeans_elbow(self): + logger.info("Process started..") + # self.update_stack() + + with pg.BusyCursor(): + try: + clust_n, var = kmeans_variance(self.displayedStack) + kmeans_var_plot = pg.plot( + clust_n, var, title="KMeans Variance", pen=pg.mkPen("y", width=2, style=QtCore.Qt.DotLine), + symbol="o" + ) + kmeans_var_plot.setLabel("bottom", "Cluster Number") + kmeans_var_plot.setLabel("left", "Sum of squared distances") + logger.info("Process complete") + except OverflowError: + pass + logger.error("Overflow Error, values are too long") + + def kmeans_elbow_Thread(self): + # Pass the function to execute + worker = Worker(self.kmeans_elbow) # Any other args, kwargs are passed to the run function + worker.signals.result.connect(self.print_output) + worker.signals.finished.connect(self.thread_complete) + # Execute + self.threadpool.start(worker) + + def clustering_(self): + + logger.info("Process started..") + # self.update_stack() + method_ = self.cb_clust_method.currentText() + + decon_images, X_cluster, decon_spectra = cluster_stack( + self.displayedStack, + method=method_, + n_clusters_=self.sb_ncluster.value(), + decomposed=False, + decompose_method=self.cb_comp_method.currentText(), + decompose_comp=self.sb_ncomp.value(), + ) + + self._new_window4 = ClusterViewer(decon_images, self.xdata, X_cluster, decon_spectra) + self._new_window4.show() + + logger.info("Process complete") + + def change_color_on_load(self, button_name): + button_name.setStyleSheet("background-color : rgb(0,150,0);" "color: rgb(255,255,255)") + + def energyFileChooser(self): + file_name = QFileDialog().getOpenFileName(self, + "Open energy list", + self.user_wd, + "text file (*.txt)") + self.efilePath = file_name[0] + + def fast_xanes_fitting(self): + + self._new_window5 = XANESViewer(self.displayedStack, self.xdata, self.refs, self.ref_names) + self._new_window5.show() + + # Thread Signals + + def print_output(self, s): + print(s) + + def thread_complete(self): + print("THREAD COMPLETE!") + + def closeEvent(self, event): + reply = QMessageBox.question( + self, + "Window Close", + "Are you sure you want to close?", + QMessageBox.Yes | QMessageBox.No, + QMessageBox.No, + ) + + if reply == QMessageBox.Yes: + event.accept() + QApplication.closeAllWindows() + else: + event.ignore() + + +class WorkerSignals(QObject): + """ + Defines the signals available from a running worker thread. + Supported signals are: + - finished: No data + - error:`tuple` (exctype, value, traceback.format_exc() ) + - result: `object` data returned from processing, anything + - progress: `tuple` indicating progress metadata + """ + + start = pyqtSignal() + finished = pyqtSignal() + error = pyqtSignal(tuple) + result = pyqtSignal(object) + + +class Worker(QRunnable): + """ + Worker thread + Inherits from QRunnable to handler worker thread setup, signals and wrap-up. + """ + + def __init__(self, fn, *args, **kwargs): + super(Worker, self).__init__() + # Store constructor arguments (re-used for processing) + self.fn = fn + self.args = args + self.kwargs = kwargs + self.signals = WorkerSignals() + + @pyqtSlot() + def run(self): + """ + Initialise the runner function with passed args, kwargs. + """ + # Retrieve args/kwargs here; and fire processing using them + self.signals.start.emit() + try: + result = self.fn(*self.args, **self.kwargs) + except Exception: + traceback.print_exc() + exctype, value = sys.exc_info()[:2] + self.signals.error.emit((exctype, value, traceback.format_exc())) + else: + self.signals.result.emit(result) # Return the result of the processing + finally: + self.signals.finished.emit() # Done + + +class singleStackViewer(QtWidgets.QMainWindow): + def __init__(self, img_stack, gradient="viridis"): + super(singleStackViewer, self).__init__() + + # Load the UI Page + uic.loadUi(os.path.join(ui_path, "uis/singleStackView.ui"), self) + self.user_wd = os.path.abspath("~") + + self.image_view.ui.menuBtn.hide() + self.image_view.ui.roiBtn.hide() + + self.img_stack = img_stack + self.gradient = gradient + self.image_view.setPredefinedGradient(gradient) + + if self.img_stack.ndim == 3: + self.dim1, self.dim3, self.dim2 = img_stack.shape + elif self.img_stack.ndim == 2: + self.dim3, self.dim2 = img_stack.shape + self.dim1 = 1 + self.hs_img_stack.setMaximum(self.dim1 - 1) + self.hs_img_stack.setValue(np.round(self.dim1 / 2)) + self.displayStack() + + # connections + self.hs_img_stack.valueChanged.connect(self.displayStack) + self.actionSave.triggered.connect(self.saveImageStackAsTIFF) + + def displayStack(self): + im_index = self.hs_img_stack.value() + if self.img_stack.ndim == 2: + self.image_view.setImage(self.img_stack) + else: + self.image_view.setImage(self.img_stack[im_index]) + self.label_img_count.setText(f"{im_index + 1}/{self.dim1}") + + def saveImageStackAsTIFF(self): + file_name = QFileDialog().getSaveFileName(self, + "Export Stack", + os.path.join(self.user_wd, "image_stack.tiff"), + "*.tiff;;*.tif") + if file_name[0]: + if self.img_stack.ndim == 3: + tf.imsave(str(file_name[0]), np.float32(self.img_stack.transpose(0, 2, 1))) + elif self.img_stack.ndim == 2: + tf.imsave(str(file_name[0]), np.float32(self.img_stack.T)) + + self.user_wd = os.path.dirname(file_name[0]) + else: + pass + + +class ComponentViewer(QtWidgets.QMainWindow): + def __init__(self, comp_stack, energy, comp_spectra, decon_spectra, decomp_map): + super(ComponentViewer, self).__init__() + + # Load the UI Page + uic.loadUi(os.path.join(ui_path, "uis/ComponentView.ui"), self) + self.centralwidget.setStyleSheet(open(os.path.join(ui_path, "css/defaultStyle.css")).read()) + self.user_wd = os.path.abspath("~") + + self.comp_stack = comp_stack + self.energy = energy + self.comp_spectra = comp_spectra + self.decon_spectra = decon_spectra + self.decomp_map = decomp_map + + (self.dim1, self.dim3, self.dim2) = self.comp_stack.shape + self.hs_comp_number.setMaximum(self.dim1 - 1) + + self.image_view.setImage(self.comp_stack) + self.image_view.setPredefinedGradient("viridis") + self.image_view.ui.menuBtn.hide() + self.image_view.ui.roiBtn.hide() + + self.image_view2.setImage(self.decomp_map) + self.image_view2.setPredefinedGradient("bipolar") + self.image_view2.ui.menuBtn.hide() + self.image_view2.ui.roiBtn.hide() + + # connection + self.update_image() + self.pb_show_all.clicked.connect(self.show_all_spec) + self.hs_comp_number.valueChanged.connect(self.update_image) + self.actionSave.triggered.connect(self.save_comp_data) + self.pb_openScatterPlot.clicked.connect(self.openScatterPlot) + self.pb_showMultiColor.clicked.connect(lambda: self.generateMultiColorView(withSpectra=False)) + self.pb_showMultiImageXANESView.clicked.connect(lambda: self.generateMultiColorView(withSpectra=True)) + + def update_image(self): + im_index = self.hs_comp_number.value() + self.spectrum_view.setLabel("bottom", "Energy") + self.spectrum_view.setLabel("left", "Intensity", "A.U.") + self.spectrum_view.plot(self.energy, self.decon_spectra[:, im_index], clear=True) + self.component_view.setLabel("bottom", "Energy") + self.component_view.setLabel("left", "Weight", "A.U.") + self.component_view.plot(self.energy, self.comp_spectra[:, im_index], clear=True) + self.label_comp_number.setText(f"{im_index + 1}/{self.dim1}") + # self.image_view.setCurrentIndex(im_index-1) + self.image_view.setImage(self.comp_stack[im_index]) + + def openScatterPlot(self): + self.scatter_window = ComponentScatterPlot(self.comp_stack, self.comp_spectra) + + # ph = self.geometry().height() + # pw = self.geometry().width() + # px = self.geometry().x() + # py = self.geometry().y() + # dw = self.scatter_window.width() + # dh = self.scatter_window.height() + # self.scatter_window.setGeometry(px+0.65*pw, py + ph - 2*dh-5, dw, dh) + self.scatter_window.show() + + def show_all_spec(self): + self.spectrum_view.clear() + self.plt_colors = ["g", "b", "r", "c", "m", "y", "w"] * 10 + offsets = np.arange(0, 2, 0.2) + self.spectrum_view.addLegend() + for ii in range(self.decon_spectra.shape[1]): + self.spectrum_view.plot( + self.energy, + (self.decon_spectra[:, ii] / self.decon_spectra[:, ii].max()) + offsets[ii], + pen=self.plt_colors[ii], + name="component" + str(ii + 1), + ) + + def save_comp_data(self): + file_name = QFileDialog().getSaveFileName(self, "save all data", self.user_wd, "data(*tiff *tif *txt *png )") + if file_name[0]: + tf.imsave( + str(file_name[0]) + "_components.tiff", np.float32(self.comp_stack.transpose(0, 2, 1)), imagej=True + ) + tf.imsave(str(file_name[0]) + "_component_masks.tiff", np.float32(self.decomp_map.T), imagej=True) + np.savetxt(str(file_name[0]) + "_deconv_spec.txt", self.decon_spectra) + np.savetxt(str(file_name[0]) + "_component_spec.txt", self.comp_spectra) + + self.user_wd = os.path.dirname(file_name[0]) + else: + pass + + def generateMultiColorView(self, withSpectra=False): + self.multichanneldict = {} + + for n, (colorName, image) in enumerate(zip(cmap_dict.keys(), self.comp_stack.transpose(0, 1, 2))): + low, high = np.min(image), np.max(image) + self.multichanneldict[f'Image {n + 1}'] = {'ImageName': f'Image {n + 1}', + 'ImageDir': '.', + 'Image': image, + 'Color': colorName, + 'CmapLimits': (low, high), + 'Opacity': 1.0 + } + + if withSpectra: + compXanesSpetraAll = pd.DataFrame() + compXanesSpetraAll['Energy'] = self.energy + + for n, spec in enumerate(self.decon_spectra.T): + compXanesSpetraAll[f'Component_{n + 1}'] = spec + + self.muli_color_window = MultiXANESWindow(image_dict=self.multichanneldict, + spec_df=compXanesSpetraAll) + else: + self.muli_color_window = MultiChannelWindow(image_dict=self.multichanneldict) + + self.muli_color_window.show() + + # add energy column + + +class ClusterViewer(QtWidgets.QMainWindow): + def __init__(self, decon_images, energy, X_cluster, decon_spectra): + super(ClusterViewer, self).__init__() + + # Load the UI Page + uic.loadUi(os.path.join(ui_path, "uis/ClusterView.ui"), self) + self.user_wd = os.path.abspath("~") + self.centralwidget.setStyleSheet(open(os.path.join(ui_path, "css/defaultStyle.css")).read()) + + self.decon_images = decon_images + self.energy = energy + self.X_cluster = X_cluster + self.decon_spectra = decon_spectra + (self.dim1, self.dim3, self.dim2) = self.decon_images.shape + self.hsb_cluster_number.setMaximum(self.dim1 - 1) + self.X_cluster = X_cluster + + self.image_view.setImage(self.decon_images, autoHistogramRange=True, autoLevels=True) + self.image_view.setPredefinedGradient("viridis") + self.image_view.ui.menuBtn.hide() + self.image_view.ui.roiBtn.hide() + + self.cluster_view.setImage(self.X_cluster, autoHistogramRange=True, autoLevels=True) + self.cluster_view.setPredefinedGradient("bipolar") + self.cluster_view.ui.histogram.hide() + self.cluster_view.ui.menuBtn.hide() + self.cluster_view.ui.roiBtn.hide() + + # connection + self.update_display() + self.hsb_cluster_number.valueChanged.connect(self.update_display) + self.actionSave.triggered.connect(self.save_clust_data) + self.pb_show_all_spec.clicked.connect(self.showAllSpec) + self.pb_showMultiColor.clicked.connect(self.generateMultiColorView) + + def update_display(self): + im_index = self.hsb_cluster_number.value() + self.component_view.setLabel("bottom", "Energy") + self.component_view.setLabel("left", "Intensity", "A.U.") + self.component_view.plot(self.energy, self.decon_spectra[:, im_index], clear=True) + # self.image_view.setCurrentIndex(im_index-1) + self.image_view.setImage(self.decon_images[im_index]) + self.label_comp_number.setText(f"{im_index + 1}/{self.dim1}") + + def save_clust_data(self): + file_name = QFileDialog().getSaveFileName(self, "", "", "data(*tiff *tif *txt *png )") + if file_name[0]: + + tf.imsave( + str(file_name[0]) + "_cluster.tiff", np.float32(self.decon_images.transpose(0, 2, 1)), imagej=True + ) + tf.imsave(str(file_name[0]) + "_cluster_map.tiff", np.float32(self.X_cluster.T), imagej=True) + np.savetxt(str(file_name[0]) + "_deconv_spec.txt", self.decon_spectra) + + else: + logger.error("Saving Cancelled") + self.statusbar.showMessage("Saving Cancelled") + pass + + def showAllSpec(self): + self.component_view.clear() + self.plt_colors = ["g", "b", "r", "c", "m", "y", "w"] * 10 + offsets = np.arange(0, 2, 0.2) + self.component_view.addLegend() + for ii in range(self.decon_spectra.shape[1]): + self.component_view.plot( + self.energy, + (self.decon_spectra[:, ii] / self.decon_spectra[:, ii].max()) + offsets[ii], + pen=self.plt_colors[ii], + name="cluster" + str(ii + 1), + ) + + def generateMultiColorView(self): + self.multichanneldict = {} + + for n, (colorName, image) in enumerate(zip(cmap_dict.keys(), self.decon_images.transpose(0, 1, 2))): + low, high = np.min(image), np.max(image) + self.multichanneldict[f"Image {n + 1}"] = { + "ImageName": f"Image {n + 1}", + "ImageDir": ".", + "Image": image, + "Color": colorName, + "CmapLimits": (low, high), + "Opacity": 1.0, + } + self.muli_color_window = MultiChannelWindow(image_dict=self.multichanneldict) + self.muli_color_window.show() + + +class XANESViewer(QtWidgets.QMainWindow): + def __init__(self, im_stack=None, e_list=None, refs=None, ref_names=None): + super(XANESViewer, self).__init__() + + uic.loadUi(os.path.join(ui_path, "uis/XANESViewer.ui"), self) + self.user_wd = os.path.abspath("~") + self.centralwidget.setStyleSheet(open(os.path.join(ui_path, "css/defaultStyle.css")).read()) + + self.im_stack = im_stack + self.e_list = e_list + self.refs = refs + self.ref_names = ref_names + self.selected = self.ref_names + self.fitResultDict = {} + self.fit_method = self.cb_xanes_fit_model.currentText() + self.alphaForLM = self.dsb_alphaForLM.value() + + self.decon_ims, self.rfactor, self.coeffs_arr = xanes_fitting( + self.im_stack, self.e_list, self.refs, method=self.fit_method, alphaForLM=self.alphaForLM + ) + + (self.dim1, self.dim2, self.dim3) = self.im_stack.shape + self.cn = int(self.dim2 // 2) + self.sz = np.max([int(self.dim2 * 0.15), int(self.dim3 * 0.15)]) + self.image_roi = pg.RectROI( + [int(self.dim3 // 2), int(self.dim2 // 2)], + [self.sz, self.sz], + pen="w", + maxBounds=QtCore.QRectF(0, 0, self.dim3, self.dim2), + ) + + self.image_roi.addTranslateHandle([0, 0], [2, 2]) + self.image_roi.addRotateHandle([0, 1], [2, 2]) + + # self.image_roi = pg.PolyLineROI([[0, 0], [0, self.sz], [self.sz, self.sz], [self.sz, 0]], + # pos=(int(self.dim2 // 2), int(self.dim3 // 2)), + # maxBounds=QtCore.QRect(0, 0, self.dim3, self.dim2), closed=True) + # self.image_roi.addTranslateHandle([self.sz // 2, self.sz // 2], [2, 2]) + + self.stack_center = int(self.dim1 // 2) + self.stack_width = int(self.dim1 * 0.05) + # self.image_view.setCurrentIndex(self.stack_center) + + self.image_view.addItem(self.image_roi) + self.xdata = self.e_list + self.sb_e_shift.value() + + self.scrollBar_setup() + self.display_image_data() + self.display_references() + self.update_spectrum() + + # connections + self.sb_e_shift.valueChanged.connect(self.update_spectrum) + self.pb_re_fit.clicked.connect(self.re_fit_xanes) + self.pb_edit_refs.clicked.connect(self.choose_refs) + self.image_roi.sigRegionChanged.connect(self.update_spectrum) + self.hsb_xanes_stk.valueChanged.connect(self.display_image_data) + self.hsb_chem_map.valueChanged.connect(self.display_image_data) + # self.pb_showMultiColor.clicked.connect(self.generateMultiColorView) + # self.pb_showCompSpec.clicked.connect(self.showComponentXANES) + self.pb_showCompSpec.clicked.connect(self.generateCompoisteImageSpectrumView) + + # menu + self.actionSave_Chem_Map.triggered.connect(self.save_chem_map) + self.actionSave_R_factor_Image.triggered.connect(self.save_rfactor_img) + #self.actionSave_Live_Fit_Data.triggered.connect(self.pg_export_spec_fit) + self.actionSave_Live_Fit_Data.triggered.connect(self.export_live_data) + self.actionExport_Fit_Stats.triggered.connect(self.exportFitResults) + self.actionExport_Ref_Plot.triggered.connect(self.pg_export_references) + + def scrollBar_setup(self): + self.hsb_xanes_stk.setValue(self.stack_center) + self.hsb_xanes_stk.setMaximum(self.dim1 - 1) + self.hsb_chem_map.setValue(0) + self.hsb_chem_map.setMaximum(self.decon_ims.shape[-1] - 1) + + def display_image_data(self): + + self.image_view.setImage(self.im_stack[self.hsb_xanes_stk.value()]) + self.image_view.ui.menuBtn.hide() + self.image_view.ui.roiBtn.hide() + self.image_view.setPredefinedGradient("viridis") + + self.image_view_maps.setImage(self.decon_ims.transpose(2, 0, 1)[self.hsb_chem_map.value()]) + self.image_view_maps.setPredefinedGradient("bipolar") + self.image_view_maps.ui.menuBtn.hide() + self.image_view_maps.ui.roiBtn.hide() + + def display_references(self): + + self.inter_ref = interploate_E(self.refs, self.xdata) + self.plt_colors = ["c", "m", "y", "w"] * 10 + self.spectrum_view_refs.addLegend() + for ii in range(self.inter_ref.shape[0]): + if len(self.selected) != 0: + self.spectrum_view_refs.plot( + self.xdata, + self.inter_ref[ii], + pen=pg.mkPen(self.plt_colors[ii], width=2), + name=self.selected[1:][ii], + ) + else: + self.spectrum_view_refs.plot( + self.xdata, + self.inter_ref[ii], + pen=pg.mkPen(self.plt_colors[ii], width=2), + name="ref" + str(ii + 1), + ) + + def choose_refs(self): + "Interactively exclude some standards from the reference file" + self.ref_edit_window = RefChooser( + self.ref_names, + self.im_stack, + self.e_list, + self.refs, + self.sb_e_shift.value(), + self.cb_xanes_fit_model.currentText(), + ) + self.ref_edit_window.show() + # self.rf_plot = pg.plot(title="RFactor Tracker") + + # connections + self.ref_edit_window.choosenRefsSignal.connect(self.update_refs) + self.ref_edit_window.fitResultsSignal.connect(self.plotFitResults) + + def update_refs(self, list_): + self.selected = list_ # list_ is the signal from ref chooser + self.update_spectrum() + self.re_fit_xanes() + + def update_spectrum(self): + + self.roi_img = self.image_roi.getArrayRegion(self.im_stack, self.image_view.imageItem, axes=(1, 2)) + sizex, sizey = self.roi_img.shape[1], self.roi_img.shape[2] + posx, posy = self.image_roi.pos() + self.roi_info.setText(f"ROI_Pos: {int(posx)},{int(posy)} ROI_Size: {sizex},{sizey}") + + self.xdata1 = self.e_list + self.sb_e_shift.value() + self.ydata1 = get_mean_spectra(self.roi_img) + self.fit_method = self.cb_xanes_fit_model.currentText() + + if len(self.selected) != 0: + + self.inter_ref = interploate_E(self.refs[self.selected], self.xdata1) + stats, coeffs = xanes_fitting_1D( + self.ydata1, + self.xdata1, + self.refs[self.selected], + method=self.fit_method, + alphaForLM=self.alphaForLM, + ) + + else: + self.inter_ref = interploate_E(self.refs, self.xdata1) + stats, coeffs = xanes_fitting_1D( + self.ydata1, self.xdata1, self.refs, method=self.fit_method, alphaForLM=self.alphaForLM + ) + + self.fit_ = np.dot(coeffs, self.inter_ref) + pen = pg.mkPen("g", width=1.5) + pen2 = pg.mkPen("r", width=1.5) + # pen3 = pg.mkPen("y", width=1.5) + self.spectrum_view.addLegend() + self.spectrum_view.setLabel("bottom", "Energy") + self.spectrum_view.setLabel("left", "Intensity", "A.U.") + self.spectrum_view.plot(self.xdata1, self.ydata1, pen=pen, name="Data", clear=True) + self.spectrum_view.plot(self.xdata1, self.fit_, name="Fit", pen=pen2) + + for n, (coff, ref, plt_clr) in enumerate(zip(coeffs, self.inter_ref, self.plt_colors)): + + if len(self.selected) != 0: + + self.spectrum_view.plot(self.xdata1, np.dot(coff, ref), name=self.selected[1:][n], pen=plt_clr) + else: + self.spectrum_view.plot(self.xdata1, np.dot(coff, ref), name="ref" + str(n + 1), pen=plt_clr) + # set the rfactor value to the line edit slot + self.results = ( + f"Coefficients: {coeffs} \n" + f"R-Factor: {stats['R_Factor']}, R-Square: {stats['R_Square']},\n " + f"Chi-Square: {stats['Chi_Square']}, " + f"Reduced Chi-Square: {stats['Reduced Chi_Square']}" + ) + + self.fit_results.setText(self.results) + + def re_fit_xanes(self): + if len(self.selected) != 0: + self.decon_ims, self.rfactor, self.coeffs_arr = xanes_fitting( + self.im_stack, + self.e_list + self.sb_e_shift.value(), + self.refs[self.selected], + method=self.cb_xanes_fit_model.currentText(), + alphaForLM=self.alphaForLM, + ) + else: + # if non athena file with no header is loaded no ref file cannot be edited + self.decon_ims, self.rfactor, self.coeffs_arr = xanes_fitting( + self.im_stack, + self.e_list + self.sb_e_shift.value(), + self.refs, + method=self.cb_xanes_fit_model.currentText(), + alphaForLM=self.alphaForLM, + ) + + # rfactor is a list of all spectra so take the mean + self.rfactor_mean = np.mean(self.rfactor) + self.image_view_maps.setImage(self.decon_ims.transpose(2, 0, 1)) + self.scrollBar_setup() + + def plotFitResults(self, decon_ims, rfactor_mean, coeff_array): + # upadte the chem maps and scrollbar params + self.image_view_maps.setImage(decon_ims.transpose(2, 0, 1)) + # self.hsb_chem_map.setValue(0) + # self.hsb_chem_map.setMaximum(decon_ims.shape[-1]-1) + + # set the rfactor value to the line edit slot + self.le_r_sq.setText(f"{rfactor_mean :.4f}") + + def showComponentXANES(self): + compNum = self.hsb_chem_map.value() + currentComp = self.decon_ims.transpose(2, 0, 1)[compNum] + currentCompMask = currentComp > 0 + yData = applyMaskGetMeanSpectrum(self.im_stack, currentCompMask) + xanes_comp_plot = pg.plot( + self.e_list + self.sb_e_shift.value(), + yData, + title=f"Component_{compNum}", + pen=pg.mkPen("y", width=2, style=QtCore.Qt.DotLine), + symbol="o", + ) + xanes_comp_plot.setLabel("bottom", "Energy (keV)") + xanes_comp_plot.setLabel("left", "Intensity") + + def plotDeconvSpectrum(self, clusterSigma=0): + + try: + self.ref_plot.close() + + except: + pass + + self.ref_plot = plot(title="Deconvoluted XANES Spectra") + self.ref_plot.setLabel("bottom", "Energy") + self.ref_plot.setLabel("left", "Intensity") + self.ref_plot.addLegend() + + for n, compImage in enumerate(self.decon_ims.transpose(2, 0, 1)): + mask = np.where(compImage > clusterSigma * np.std(compImage), compImage, 0) + + self.ref_plot.plot( + self.xdata1, + get_mean_spectra(self.im_stack * mask), + pen=pg.mkPen(self.plt_colors[n], width=2), + name=f'Component_{n + 1}' + ) + + def generateCompoisteImageSpectrumView(self): + self.multichanneldict = {} + + spectrumDF = getDeconvolutedXANESSpectrum(self.im_stack, self.decon_ims.transpose(2, 0, 1), + self.xdata1, clusterSigma=3) + + for n, (colorName, image) in enumerate(zip(cmap_dict.keys(), self.decon_ims.transpose((2, 0, 1)))): + low, high = np.min(image), np.max(image) + self.multichanneldict[f'Image {n + 1}'] = {'ImageName': f'Image {n + 1}', + 'ImageDir': '.', + 'Image': image, + 'Color': colorName, + 'CmapLimits': (low, high), + 'Opacity': 1.0 + } + self.muli_color_window = MultiXANESWindow(image_dict=self.multichanneldict, spec_df=spectrumDF) + self.muli_color_window.show() + + def generateMultiColorView(self): + self.multichanneldict = {} + + for n, (colorName, image) in enumerate(zip(cmap_dict.keys(), self.decon_ims.transpose((2, 0, 1)))): + low, high = np.min(image), np.max(image) + self.multichanneldict[f"Image {n + 1}"] = { + "ImageName": f"Image {n + 1}", + "ImageDir": ".", + "Image": image, + "Color": colorName, + "CmapLimits": (low, high), + "Opacity": 1.0, + } + self.muli_color_window = MultiChannelWindow(image_dict=self.multichanneldict) + self.muli_color_window.show() + + def save_chem_map(self): + file_name = QFileDialog().getSaveFileName(self, + "save image", + os.path.join(self.user_wd,"chemical_map.tiff"), + "image data (*tiff)") + if file_name[0]: + tf.imsave(str(file_name[0]), np.float32(self.decon_ims.transpose(2, 0, 1)), imagej=True) + self.user_wd = os.path.dirname(file_name[0]) + else: + logger.error("No file to save") + pass + + def save_rfactor_img(self): + file_name = QFileDialog().getSaveFileName(self, + "save image", + os.path.join(self.user_wd,"r-factor_map.tiff"), + "image data (*tiff)") + if file_name[0]: + tf.imsave(str(file_name[0]), np.float32(self.rfactor), imagej=True) + self.user_wd = os.path.dirname(file_name[0]) + else: + logger.error("No file to save") + pass + + def save_spec_fit(self): + try: + to_save = np.column_stack([self.xdata1, self.ydata1, self.fit_]) + file_name = QFileDialog().getSaveFileName(self, + "save spectrum", + os.path.join(self.user_wd,"spec_fit.txt"), + "spectrum and fit (*txt)") + if file_name[0]: + np.savetxt(str(file_name[0]) + ".txt", to_save) + self.user_wd = os.path.dirname(file_name[0]) + else: + pass + except Exception: + logger.error("No file to save") + pass + + def pg_export_spec_fit(self): + + exporter = pg.exporters.CSVExporter(self.spectrum_view.plotItem) + exporter.parameters()["columnMode"] = "(x,y,y,y) for all plots" + file_name = QFileDialog().getSaveFileName(self, + "save spectrum", + os.path.join(self.user_wd,"spec_fit.txt"), + "spectrum and fit (*csv)") + if file_name[0]: + exporter.export(str(file_name[0]) + ".csv") + self.user_wd = os.path.dirname(file_name[0]) + else: + pass + + def pg_export_references(self): + + exporter = pg.exporters.CSVExporter(self.spectrum_view_refs.plotItem) + exporter.parameters()["columnMode"] = "(x,y,y,y) for all plots" + file_name = QFileDialog().getSaveFileName(self, + "save references", + os.path.join(self.user_wd,"xanes_references.csv"), + "column data (*csv)" + ) + if file_name[0]: + exporter.export(str(file_name[0])) + self.user_wd = os.path.dirname(file_name[0]) + else: + pass + + def exportFitResults(self): + file_name = QFileDialog().getSaveFileName(self, + "save txt", + os.path.join(self.user_wd,"xanes_1D_fit_results.txt"), + "txt data (*txt)") + if file_name[0]: + with open(file_name[0], "w") as file: + file.write(self.results) + self.user_wd = os.path.dirname(file_name[0]) + else: + pass + + def export_live_data(self): + file_name = QFileDialog().getSaveFileName(self, + "save all live data", + os.path.join(self.user_wd,"xanes_fit"), + "All Files (*)") + exporter_csv = pg.exporters.CSVExporter(self.spectrum_view.plotItem) + exporter_csv.parameters()["columnMode"] = "(x,y,y,y) for all plots" + exporter_png = pg.exporters.ImageExporter(self.spectrum_view.getViewBox()) + exporter_img_png = pg.exporters.ImageExporter(self.image_view.getView()) + if file_name[0]: + exporter_csv.export(str(file_name[0]+"_data.csv")) + exporter_png.export(str(file_name[0]+"_spec_image.png")) + exporter_img_png.export(str(file_name[0]+"_area_map.png")) + with open(file_name[0]+"_params.txt", "w") as file: + file.write(self.results) + self.user_wd = os.path.dirname(file_name[0]) + + +class RefChooser(QtWidgets.QMainWindow): + choosenRefsSignal: pyqtSignal = QtCore.pyqtSignal(list) + fitResultsSignal: pyqtSignal = QtCore.pyqtSignal(np.ndarray, float, np.ndarray) + + def __init__(self, ref_names, im_stack, e_list, refs, e_shift, fit_model): + super(RefChooser, self).__init__() + uic.loadUi(os.path.join(ui_path, "uis/RefChooser.ui"), self) + self.user_wd = os.path.abspath("~") + self.centralwidget.setStyleSheet(open(os.path.join(ui_path, "css/defaultStyle.css")).read()) + self.ref_names = ref_names + self.refs = refs + self.im_stack = im_stack + self.e_list = e_list + self.e_shift = e_shift + self.fit_model = fit_model + + self.all_boxes = [] + self.rFactorList = [] + + self.displayCombinations() + + # selection become more apparent than default with red-ish color + self.tableWidget.setStyleSheet("background-color: white; selection-background-color: rgb(200,0,0);") + + # add a line to the plot to walk through the table. Note that the table is not sorted + self.selectionLine = pg.InfiniteLine( + pos=1, angle=90, pen=pg.mkPen("m", width=2.5), movable=True, bounds=None, label="Move Me!" + ) + self.stat_view.setLabel("bottom", "Fit ID") + self.stat_view.setLabel("left", "Reduced Chi^2") + + for n, i in enumerate(self.ref_names): + self.cb_i = QtWidgets.QCheckBox(self.ref_box_frame) + if n == 0: + self.cb_i.setChecked(True) + self.cb_i.setEnabled(False) + self.cb_i.setObjectName(i) + self.cb_i.setText(i) + self.gridLayout_2.addWidget(self.cb_i, n, 0, 1, 1) + self.cb_i.toggled.connect(self.enableApply) + self.all_boxes.append(self.cb_i) + + # connections + self.pb_apply.clicked.connect(self.clickedWhichAre) + self.pb_combo.clicked.connect(self.tryAllCombo) + self.actionExport_Results_csv.triggered.connect(self.exportFitResults) + self.selectionLine.sigPositionChanged.connect(self.updateFitWithLine) + self.tableWidget.itemSelectionChanged.connect(self.updateWithTableSelection) + # self.stat_view.scene().sigMouseClicked.connect(self.moveSelectionLine) + self.stat_view.mouseDoubleClickEvent = self.moveSelectionLine + self.sb_max_combo.valueChanged.connect(self.displayCombinations) + # self.pb_sort_with_r.clicked.connect(lambda: self.tableWidget.sortItems(3, QtCore.Qt.AscendingOrder)) + self.pb_sort_with_r.clicked.connect(self.sortTable) + self.cb_sorter.currentTextChanged.connect(self.sortTable) + + def clickedWhich(self): + button_name = self.sender() + + def populateChecked(self): + self.onlyCheckedBoxes = [] + for names in self.all_boxes: + if names.isChecked(): + self.onlyCheckedBoxes.append(names.objectName()) + + QtCore.pyqtSlot() + + def clickedWhichAre(self): + self.populateChecked() + self.choosenRefsSignal.emit(self.onlyCheckedBoxes) + + def generateRefList(self, ref_list, maxCombo, minCombo=1): + + """ + Creates a list of reference combinations for xanes fitting + + Paramaters; + + ref_list (list): list of ref names from the header + maxCombo (int): maximum number of ref lists in combination + minCombo (int): min number of ref lists in combination + + returns; + + 1. int: length of total number of combinations + 2. list: all the combinations + + """ + + if not maxCombo > len(ref_list): + + iter_list = [] + while minCombo < maxCombo + 1: + iter_list += list(combinations(ref_list, minCombo)) + minCombo += 1 + return len(iter_list), iter_list + + else: + raise ValueError(" Maximum numbinations cannot be larger than number of list items") + + def displayCombinations(self): + niter, self.iter_list = self.generateRefList(self.ref_names[1:], self.sb_max_combo.value()) + self.label_nComb.setText(str(niter) + " Combinations") + + @QtCore.pyqtSlot() + def tryAllCombo(self): + # empty list to to keep track and plot of reduced chi2 of all the fits + self.rfactor_list = [] + + # create dataframe for the table + self.df = pd.DataFrame( + columns=["Fit Number", "References", "Coefficients", "R-Factor", "R^2", "chi^2", "red-chi^2", "Score"] + ) + + # df columns is the header for the table widget + self.tableWidget.setHorizontalHeaderLabels(self.df.columns) + # self.iter_list = list(combinations(self.ref_names[1:],self.sb_max_combo.value())) + + niter, self.iter_list = self.generateRefList(self.ref_names[1:], self.sb_max_combo.value()) + tot_combo = len(self.iter_list) + for n, refs in enumerate(self.iter_list): + self.statusbar.showMessage(f"{n + 1}/{tot_combo}") + selectedRefs = list((str(self.ref_names[0]),) + refs) + self.fit_combo_progress.setValue((n + 1) * 100 / tot_combo) + self.stat, self.coeffs_arr = xanes_fitting_Binned( + self.im_stack, self.e_list + self.e_shift, self.refs[selectedRefs], method=self.fit_model + ) + + self.rfactor_list.append(self.stat["Reduced Chi_Square"]) + self.stat_view.plot( + x=np.arange(n + 1), + y=self.rfactor_list, + clear=True, + title="Reduced Chi^2", + pen=pg.mkPen("y", width=2, style=QtCore.Qt.DotLine), + symbol="o", + ) + + # arbitary number to rank the best fit + fit_score = (self.stat["R_Square"] + np.sum(self.coeffs_arr)) / ( + self.stat["R_Factor"] + self.stat["Reduced Chi_Square"] + ) + + resultsDict = { + "Fit Number": n, + "References": str(selectedRefs[1:]), + "Coefficients": str(np.around(self.coeffs_arr, 4)), + "Sum of Coefficients": str(np.around(np.sum(self.coeffs_arr), 4)), + "R-Factor": self.stat["R_Factor"], + "R^2": self.stat["R_Square"], + "chi^2": self.stat["Chi_Square"], + "red-chi^2": self.stat["Reduced Chi_Square"], + "Score": np.around(fit_score, 4), + } + + self.df = pd.concat([self.df, pd.DataFrame([resultsDict])], ignore_index=True) + + self.dataFrametoQTable(self.df) + QtTest.QTest.qWait(0.1) # hepls with real time plotting + + self.stat_view.addItem(self.selectionLine) + + def dataFrametoQTable(self, df_: pd.DataFrame): + nRows = len(df_.index) + nColumns = len(df_.columns) + self.tableWidget.setRowCount(nRows) + self.tableWidget.setColumnCount(nColumns) + self.tableWidget.setHorizontalHeaderLabels(df_.columns) + + for i in range(nRows): + for j in range(nColumns): + cell = QtWidgets.QTableWidgetItem(str(df_.values[i][j])) + self.tableWidget.setItem(i, j, cell) + + # set the property of the table view. Size policy to make the contents justified + self.tableWidget.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents) + self.tableWidget.resizeColumnsToContents() + + def exportFitResults(self): + file_name = QFileDialog().getSaveFileName(self, "save csv", "xanes_fit_results_log.csv", "txt data (*csv)") + if file_name[0]: + with open(str(file_name[0]), "w") as fp: + self.df.to_csv(fp) + else: + pass + + def selectTableAndCheckBox(self, x): + nSelection = int(round(x)) + self.tableWidget.selectRow(nSelection) + fit_num = int(self.tableWidget.item(nSelection, 0).text()) + refs_selected = self.iter_list[fit_num] + + # reset all the checkboxes to uncheck state, except the energy + for checkstate in self.findChildren(QtWidgets.QCheckBox): + if checkstate.isEnabled(): + checkstate.setChecked(False) + + for cb_names in refs_selected: + checkbox = self.findChild(QtWidgets.QCheckBox, name=cb_names) + checkbox.setChecked(True) + + def updateFitWithLine(self): + pos_x, pos_y = self.selectionLine.pos() + x = self.df.index[self.df[str("Fit Number")] == np.round(pos_x)][0] + self.selectTableAndCheckBox(x) + + def updateWithTableSelection(self): + x = self.tableWidget.currentRow() + self.selectTableAndCheckBox(x) + + def moveSelectionLine(self, event): + if event.button() == QtCore.Qt.LeftButton: + Pos = self.stat_view.plotItem.vb.mapSceneToView(event.pos()) + self.selectionLine.setPos(Pos.x()) + + def sortTable(self): + sorter_dict = { + "R-Factor": "R-Factor", + "R-Square": "R^2", + "Chi-Square": "chi^2", + "Reduced Chi-Square": "red-chi^2", + "Fit Number": "Fit Number", + } + sorter = sorter_dict[self.cb_sorter.currentText()] + self.df = self.df.sort_values(sorter, ignore_index=True) + self.dataFrametoQTable(self.df) + + def enableApply(self): + + """ """ + self.populateChecked() + if len(self.onlyCheckedBoxes) > 1: + self.pb_apply.setEnabled(True) + else: + self.pb_apply.setEnabled(False) + +class ScatterPlot(QtWidgets.QMainWindow): + def __init__(self, img1, img2, nameTuple): + super(ScatterPlot, self).__init__() + + uic.loadUi(os.path.join(ui_path, "uis/ScatterView.ui"), self) + self.user_wd = os.path.abspath("~") + self.centralwidget.setStyleSheet(open(os.path.join(ui_path, "css/defaultStyle.css")).read()) + self.clearPgPlot() + self.w1 = self.scatterViewer.addPlot() + self.img1 = img1 + self.img2 = img2 + self.nameTuple = nameTuple + x, y = np.shape(self.img1) + self.s1 = pg.ScatterPlotItem(size=2, pen=pg.mkPen(None), brush=pg.mkBrush(255, 255, 0, 255)) + # print(self.s1) + + # create three polyline ROIs for masking + Xsize = self.img1.max() / 6 + Ysize = self.img2.max() / 6 + + self.scatter_mask = pg.PolyLineROI( + [[0, 0], [0, Ysize], [Xsize / 2, Ysize * 1.5], [Xsize, Ysize], [Xsize, 0]], + pos=None, + pen=pg.mkPen("r", width=2), + hoverPen=pg.mkPen("w", width=2), + closed=True, + removable=True, + ) + + self.scatter_mask2 = pg.PolyLineROI( + [ + [Xsize * 1.2, 0], + [Xsize * 1.2, Ysize * 2], + [Xsize * 2, Ysize * 2], + [Xsize * 3, Ysize], + [Xsize * 2, 0], + ], + pos=None, + pen=pg.mkPen("g", width=2), + hoverPen=pg.mkPen("w", width=2), + closed=True, + removable=True, + ) + self.scatter_mask3 = pg.PolyLineROI( + [ + [Xsize * 2.5, 0], + [Xsize * 2.5, Ysize], + [Xsize * 4, Ysize], + [Xsize * 4, 0], + [Xsize * 3.7, Ysize * -0.5], + ], + pos=None, + pen=pg.mkPen("c", width=2), + hoverPen=pg.mkPen("w", width=2), + closed=True, + removable=True, + ) + + self.fitScatter = self.fitScatter2 = self.fitScatter3 = None + + self.rois = { + "ROI 1": (self.scatter_mask, self.rb_roi1.isChecked(), self.fitScatter), + "ROI 2": (self.scatter_mask2, self.rb_roi2.isChecked(), self.fitScatter2), + "ROI 3": (self.scatter_mask3, self.rb_roi3.isChecked(), self.fitScatter3), + } + + self.windowNames = {"ROI 1": self.fitScatter, "ROI 2": self.fitScatter2, "ROI 3": self.fitScatter3} + + self.s1.setData(self.img1.flatten(), self.img2.flatten()) + self.w1.setLabel("bottom", self.nameTuple[0], "counts") + self.label_img1.setText(self.nameTuple[0]) + self.w1.setLabel("left", self.nameTuple[1], "counts") + self.label_img2.setText(self.nameTuple[1]) + self.w1.addItem(self.s1) + + self.image_view.setImage(self.img1) + self.image_view.ui.menuBtn.hide() + self.image_view.ui.roiBtn.hide() + self.image_view.setPredefinedGradient("thermal") + + self.image_view2.setImage(self.img2) + self.image_view2.ui.menuBtn.hide() + self.image_view2.ui.roiBtn.hide() + self.image_view2.setPredefinedGradient("thermal") + + # connections + self.actionSave_Plot.triggered.connect(self.pg_export_correlation) + self.actionSave_Images.triggered.connect(self.tiff_export_images) + # self.pb_define_mask.clicked.connect(lambda:self.createMask(self.scatter_mask)) + self.pb_define_mask.clicked.connect(self.addMultipleROIs) + # self.pb_apply_mask.clicked.connect(lambda:self.getMaskRegion(self.scatter_mask)) + self.pb_apply_mask.clicked.connect(self.applyMultipleROIs) + self.pb_clear_mask.clicked.connect(self.clearMultipleROIs) + self.pb_compositeScatter.clicked.connect(self.createCompositeScatter) + [rbs.clicked.connect(self.updateROIDict) for rbs in [self.rb_roi1, self.rb_roi2, self.rb_roi3]] + + def pg_export_correlation(self): + + exporter = pg.exporters.CSVExporter(self.w1) + exporter.parameters()["columnMode"] = "(x,y,y,y) for all plots" + file_name = QFileDialog().getSaveFileName(self, + "save correlation", + os.path.join(self.user_wd,"correlation.csv"), + "spectrum and fit (*csv)") + if file_name[0]: + exporter.export(str(file_name[0]) + ".csv") + self.statusbar.showMessage(f"Data saved to {str(file_name[0])}") + self.user_wd = os.path.dirname(file_name[0]) + else: + pass + + def tiff_export_images(self): + file_name = QFileDialog().getSaveFileName(self, + "save images", + os.path.join(self.user_wd,"image.txt"), + "spectrum and fit (*tiff)") + if file_name[0]: + tf.imsave(str(file_name[0]) + ".tiff", np.dstack([self.img1, self.img2]).T) + self.statusbar.showMessage(f"Images saved to {str(file_name[0])}") + self.user_wd = os.path.dirname(file_name[0]) + else: + pass + + def createMask(self, ROIName): + + try: + self.w1.removeItem(ROIName) + except Exception: + pass + self.w1.addItem(ROIName) + + def clearMask(self, ROIName): + self.w1.removeItem(ROIName) + + def clearPgPlot(self): + try: + self.masked_img.close() + except Exception: + pass + + def getMaskRegion(self, ROIName, generateSeperateWindows=True): + + """filter scatterplot points using polylineROI region""" + + # Ref : https://stackoverflow.com/questions/57719303/how-to-map-mouse-position-on-a-scatterplot + + # get the roi region:QPaintPathObject + roiShape = self.rois[ROIName][0].mapToItem(self.s1, self.rois[ROIName][0].shape()) + + # get data in the scatter plot + scatterData = np.array(self.s1.getData()) + + # generate a binary mask for points inside or outside the roishape + selected = [roiShape.contains(QtCore.QPointF(pt[0], pt[1])) for pt in scatterData.T] + + # reshape the mask to image dimensions + self.mask2D = np.reshape(selected, (self.img1.shape)) + + # get masked image1 + self.maskedImage = self.mask2D * self.img1 + + # get rid of the (0,0) values in the masked array + self.xData, self.yData = np.compress(selected, scatterData[0]), np.compress(selected, scatterData[1]) + + # linear regeression of the filtered X,Y data + result = linregress(self.xData, self.yData) + + # Pearson's correlation of the filtered X,Y data + pr, pp = stats.pearsonr(self.xData, self.yData) + + # apply the solved equation to xData to generate the fit line + self.yyData = result.intercept + result.slope * self.xData + + # Prepare strings for fit results and stats + self.fitLineEqn = ( + f" y = x*{result.slope :.3e} + {result.intercept :.3e}," + f"\n R^2 = {result.rvalue**2 :.3f}, r = {pr :.3f}" + ) + FitStats1 = f" Slope Error = {result.stderr :.3e}, Intercept Error = {result.intercept_stderr :.3e}\n" + FitStats2 = f" Pearson’s correlation coefficient = {pr :.3f}" + refs = "\n\n ***References****\n\n scipy.stats.linregress, scipy.stats.pearsonr " + fitStats = ( + f"\n ***{ROIName} Fit Results***\n\n" + " Equation: " + self.fitLineEqn + FitStats1 + FitStats2 + refs + ) + + # generate new window to plot the results + + if generateSeperateWindows: + self.windowNames[ROIName] = MaskedScatterPlotFit( + [self.xData, self.yData], + [self.xData, self.yyData], + self.mask2D, + self.maskedImage, + fitStats, + self.fitLineEqn, + self.nameTuple, + ) + self.windowNames[ROIName].show() + + """ + from scipy.linalg import lstsq + M = xData[:, np.newaxis]**[0, 1] #use >1 for polynomial fits + p, res, rnk, s = lstsq(M, yData) + yyData = p[0] + p[1]*xData + """ + + def updateROIDict(self): + self.rois = { + "ROI 1": (self.scatter_mask, self.rb_roi1.isChecked()), + "ROI 2": (self.scatter_mask2, self.rb_roi2.isChecked()), + "ROI 3": (self.scatter_mask3, self.rb_roi3.isChecked()), + } + + def applyMultipleROIs(self): + with pg.BusyCursor(): + self.updateROIDict() + for key in self.rois.keys(): + if self.rois[key][1]: + self.getMaskRegion(key) + else: + pass + + def addMultipleROIs(self): + self.updateROIDict() + for key in self.rois.keys(): + if self.rois[key][1]: + self.createMask(self.rois[key][0]) + else: + self.clearMask(self.rois[key][0]) + + def clearMultipleROIs(self): + self.updateROIDict() + for key in self.rois.keys(): + if not self.rois[key][1]: + self.clearMask(self.rois[key][0]) + else: + pass + + def createCompositeScatter(self): + + points = [] + fitLine = [] + masks = [] + roiFitEqn = {} + + self.updateROIDict() + for n, key in enumerate(self.rois.keys()): + if self.rois[key][1]: + self.getMaskRegion(key, generateSeperateWindows=False) + points.append(np.column_stack([self.xData, self.yData])) + fitLine.append(np.column_stack([self.xData, self.yyData])) + masks.append(self.mask2D) + roiFitEqn[key] = self.fitLineEqn + else: + pass + + self.compositeScatterWindow = CompositeScatterPlot( + points, + fitLine, + np.array(masks), + roiFitEqn, + self.nameTuple + ) + self.compositeScatterWindow.show() + + def _createCompositeScatter(self): + self.scatterColors = ["w", "c", "y", "k", "m"] + points = [] + fitLine = [] + + self.updateROIDict() + for n, key in enumerate(self.rois.keys()): + if self.rois[key][1]: + self.getMaskRegion(key, generateSeperateWindows=False) + + for x, y, yy in zip(self.xData, self.yData, self.yyData): + + points.append( + { + "pos": (x, y), + "data": "id", + "size": 3, + "pen": pg.mkPen(None), + "brush": self.scatterColors[n], + } + ) + fitLine.extend(np.column_stack((self.xData, self.yyData))) + else: + pass + + logger.info(f" fitline shape: {np.shape(fitLine)}") + self.compositeScatterWindow = CompositeScatterPlot(points, np.array(fitLine)) + self.compositeScatterWindow.show() + + def getROIParams(self): + print(np.array(self.scatter_mask.getSceneHandlePositions())) + + +class MaskedScatterPlotFit(QtWidgets.QMainWindow): + def __init__(self, scatterData, fitData, mask, maskedImage, fitString, fitEquation, nameTuple): + super(MaskedScatterPlotFit, self).__init__() + + uic.loadUi(os.path.join(ui_path, "uis/maskedScatterPlotFit.ui"), self) + self.user_wd = os.path.abspath("~") + self.centralwidget.setStyleSheet(open(os.path.join(ui_path, "css/defaultStyle.css")).read()) + self.scatterData = scatterData + self.fitData = fitData + self.mask = mask + self.maskedImage = maskedImage + self.fitString = fitString + self.fitEquation = fitEquation + self.nameTuple = nameTuple + + # set the graphicslayoutwidget in the ui as canvas + self.canvas = self.scatterViewer.addPlot() + self.canvas.addLegend() + self.canvas.setLabel("bottom", self.nameTuple[0], "counts") + self.canvas.setLabel("left", self.nameTuple[1], "counts") + self.gb_maskedImage1.setTitle(f" Masked {self.nameTuple[0]}") + + # generate a scatter plot item + self.scattered = pg.ScatterPlotItem(size=3.5, pen=pg.mkPen(None), brush=pg.mkBrush(5, 214, 255, 200)) + + # set scatter plot data + self.scattered.setData(scatterData[0], scatterData[1], name="Data") + + # set z value negative to show scatter data behind the fit line + self.scattered.setZValue(-10) + + # add scatter plot to the canvas + self.canvas.addItem(self.scattered) + + # generate plotitem for fit line + self.fitLinePlot = pg.PlotDataItem(pen=pg.mkPen(pg.mkColor(220, 20, 60), width=3.3)) + + # set line plot data + self.fitLinePlot.setData(fitData[0], fitData[1], name="Linear Fit") + + # add line plot to the canvas + self.canvas.addItem(self.fitLinePlot) + + # display Mask + self.imageView_mask.setImage(self.mask) + self.imageView_mask.ui.menuBtn.hide() + self.imageView_mask.ui.roiBtn.hide() + self.imageView_mask.setPredefinedGradient("plasma") + + # display masked Image + self.imageView_maskedImage.setImage(self.maskedImage) + self.imageView_maskedImage.ui.menuBtn.hide() + self.imageView_maskedImage.ui.roiBtn.hide() + self.imageView_maskedImage.setPredefinedGradient("viridis") + + # display Fit stats + self.text_fit_results.setPlainText(fitString) + self.canvas.setTitle(self.fitEquation, color="r") + + # connections + self.pb_copy_results.clicked.connect(self.copyFitResults) + self.pb_save_results.clicked.connect(self.saveFitResults) + self.actionSave_Plot.triggered.connect(self.pg_export_correlation) + self.actionSaveMask.triggered.connect(self.saveMask) + self.actionSaveMaskedImage.triggered.connect(self.saveImage) + + def saveFitResults(self): + S__File = QFileDialog.getSaveFileName(self, "save txt", "correlationPlotFit.txt", "txt data (*txt)") + + Text = self.text_fit_results.toPlainText() + if S__File[0]: + with open(S__File[0], "w") as file: + file.write(Text) + + def copyFitResults(self): + self.text_fit_results.selectAll() + self.text_fit_results.copy() + self.statusbar.showMessage("text copied to clipboard") + + def pg_export_correlation(self): + + exporter = pg.exporters.CSVExporter(self.canvas) + exporter.parameters()["columnMode"] = "(x,y,y,y) for all plots" + file_name = QFileDialog().getSaveFileName( + self, "save correlation", "scatterData.csv", "spectrum and fit (*csv)" + ) + if file_name[0]: + exporter.export(str(file_name[0])) + self.statusbar.showMessage(f"Data saved to {str(file_name[0])}") + else: + pass + + def saveImage(self): + + file_name = QFileDialog().getSaveFileName(self, "Save image data", "image.tiff", "image file(*tiff *tif )") + if file_name[0]: + tf.imsave(str(file_name[0]), self.maskedImage) + self.statusbar.showMessage(f"Data saved to {str(file_name[0])}") + else: + self.statusbar.showMessage("Saving cancelled") + pass + + def saveMask(self): + + file_name = QFileDialog().getSaveFileName(self, "Save image data", "mask.tiff", "image file(*tiff *tif )") + if file_name[0]: + tf.imsave(str(file_name[0]), self.mask) + self.statusbar.showMessage(f"Data saved to {str(file_name[0])}") + else: + self.statusbar.showMessage("Saving cancelled") + pass + +class ComponentScatterPlot(QtWidgets.QMainWindow): + def __init__(self, decomp_stack, specs): + super(ComponentScatterPlot, self).__init__() + + uic.loadUi(os.path.join(ui_path, "uis/ComponentScatterPlot.ui"), self) + self.user_wd = os.path.abspath("~") + self.centralwidget.setStyleSheet(open(os.path.join(ui_path, "css/defaultStyle.css")).read()) + self.w1 = self.scatterViewer.addPlot() + self.decomp_stack = decomp_stack + self.specs = specs + (self.dim1, self.dim3, self.dim2) = self.decomp_stack.shape + # fill the combonbox depending in the number of components for scatter plot + for n, combs in enumerate(combinations(np.arange(self.dim1), 2)): + self.cb_scatter_comp.addItem(str(combs)) + self.cb_scatter_comp.setItemData(n, combs) + + self.s1 = pg.ScatterPlotItem(size=3, pen=pg.mkPen(None), brush=pg.mkBrush(255, 255, 0, 120)) + + self.setImageAndScatterPlot() + # connections + self.actionSave_Plot.triggered.connect(self.pg_export_correlation) + self.actionSave_Images.triggered.connect(self.tiff_export_images) + self.pb_updateComponents.clicked.connect(self.setImageAndScatterPlot) + self.pb_define_mask.clicked.connect(self.createMask) + self.pb_apply_mask.clicked.connect(self.getMaskRegion) + self.pb_reset_mask.clicked.connect(self.resetMask) + self.pb_addALine.clicked.connect(lambda: self.createMask(Line=True)) + + def setImageAndScatterPlot(self): + + try: + self.s1.clear() + except Exception: + pass + + comp_tuple = self.cb_scatter_comp.currentData() + self.img1, self.img2 = self.decomp_stack[comp_tuple[0]], self.decomp_stack[comp_tuple[-1]] + self.image_view.setImage(self.decomp_stack[comp_tuple[0]]) + self.image_view.ui.menuBtn.hide() + self.image_view.ui.roiBtn.hide() + self.image_view.setPredefinedGradient("bipolar") + + self.image_view2.setImage(self.decomp_stack[comp_tuple[-1]]) + self.image_view2.ui.menuBtn.hide() + self.image_view2.ui.roiBtn.hide() + self.image_view2.setPredefinedGradient("bipolar") + + points = [] + for i, j in zip(self.img1.flatten(), self.img2.flatten()): + + points.append( + { + "pos": (i, j), + "data": "id", + "size": 5, + "pen": pg.mkPen(None), + "brush": pg.mkBrush(255, 255, 0, 160), + } + ) + + self.s1.addPoints(points) + self.w1.addItem(self.s1) + # self.s1.setData(self.specs[:, comp_tuple[0]], self.specs[:, comp_tuple[-1]]) + self.w1.setLabel("bottom", f"PC{comp_tuple[0]+1}") + self.w1.setLabel("left", f"PC{comp_tuple[-1]+1}") + self.label_im1.setText(f"PC{comp_tuple[0]+1}") + self.label_im2.setText(f"PC{comp_tuple[-1]+1}") + + def createMask(self, Line=False): + + self.size = self.img1.max() / 10 + self.pos = int(self.img1.mean()) + + if Line: + self.lineROI = pg.LineSegmentROI( + [0, 1], + pos=(self.pos, self.pos), + pen=pg.mkPen("r", width=4), + hoverPen=pg.mkPen("g", width=4), + removable=True, + ) + self.w1.addItem(self.lineROI) + + else: + + self.scatter_mask = pg.PolyLineROI( + [[0, 0], [0, self.size], [self.size, self.size], [self.size, 0]], + pos=(self.pos, self.pos), + pen=pg.mkPen("r", width=4), + hoverPen=pg.mkPen("g", width=4), + closed=True, + removable=True, + ) + + self.w1.addItem(self.scatter_mask) + + def resetMask(self): + self.clearMask() + self.createMask() + + def clearMask(self): + try: + self.w1.removeItem(self.scatter_mask) + except AttributeError: + pass + + def clearPgPlot(self): + try: + self.masked_img.close() + except Exception: + pass + + def getMaskRegion(self): + + # Ref : https://stackoverflow.com/questions/57719303/how-to-map-mouse-position-on-a-scatterplot + + roiShape = self.scatter_mask.mapToItem(self.s1, self.scatter_mask.shape()) + self._points = list() + logger.info("Building Scatter Plot Window; Please wait..") + for i in range(len(self.img1.flatten())): + self._points.append(QtCore.QPointF(self.img1.flatten()[i], self.img2.flatten()[i])) + + selected = [roiShape.contains(pt) for pt in self._points] + img_selected = np.reshape(selected, (self.img1.shape)) + + self.masked_img = singleStackViewer(img_selected * self.img1, gradient="bipolar") + self.masked_img.show() + + def pg_export_correlation(self): + + exporter = pg.exporters.CSVExporter(self.w1) + exporter.parameters()["columnMode"] = "(x,y,y,y) for all plots" + file_name = QFileDialog().getSaveFileName(self, "save correlation", "", "spectrum and fit (*csv)") + if file_name[0]: + exporter.export(str(file_name[0]) + ".csv") + self.statusbar.showMessage(f"Data saved to {str(file_name[0])}") + else: + pass + + def tiff_export_images(self): + file_name = QFileDialog().getSaveFileName(self, "save images", "", "spectrum and fit (*tiff)") + if file_name[0]: + tf.imsave(str(file_name[0]) + ".tiff", np.dstack([self.img1, self.img2]).T) + self.statusbar.showMessage(f"Images saved to {str(file_name[0])}") + else: + pass + +class LoadingScreen(QtWidgets.QSplashScreen): + def __init__(self): + super(LoadingScreen, self).__init__() + uic.loadUi(os.path.join(ui_path, "uis/animationWindow.ui"), self) + self.setWindowOpacity(0.65) + self.movie = QMovie("uis/animation.gif") + self.label.setMovie(self.movie) + + def mousePressEvent(self, event): + # disable default "click-to-dismiss" behaviour + pass + + def startAnimation(self): + self.movie.start() + self.show() + + def stopAnimation(self): + self.movie.stop() + self.hide() + +class CompositeScatterPlot(QtWidgets.QMainWindow): + def __init__(self, scatterPoints, fitLine, maskImages, fitEquations, nameTuple): + super(CompositeScatterPlot, self).__init__() + + uic.loadUi(os.path.join(ui_path, "uis/multipleScatterFit.ui"), self) + self.user_wd = os.path.abspath("~") + self.centralwidget.setStyleSheet(open(os.path.join(ui_path, "css/defaultStyle.css")).read()) + + self.scatterPoints = scatterPoints + # print(f"{np.shape(self.scatterPoints) = }") + self.fitLine = fitLine + #print(f"{np.shape(self.fitLine) = }") + self.scatterColors = ["r", (0, 115, 0), (4, 186, 186), "c", "w", "k"] + self.fitColors = ["b", "r", "m", "k", "b"] + self.roiNames = list(fitEquations.keys()) + self.fitEqns = list(fitEquations.values()) + # print(f"{np.shape(self.roiNames) = }") + # print(f"{np.shape(self.fitEqns) = }") + self.nameTuple = nameTuple + self.maskImages = maskImages + + # self.scatterViewer.setBackground('w') + # set the graphicslayoutwidget in the ui as canvas + self.canvas = self.scatterViewer.addPlot() + self.canvas.addLegend() + self.canvas.setLabel("bottom", self.nameTuple[0], "counts") + self.canvas.setLabel("left", self.nameTuple[1], "counts") + + # connections + self.actionExport.triggered.connect(self.exportData) + self.actionSave_as_PNG.triggered.connect(self.exportAsPNG) + self.actionGenerate_MultiColor_Mask.triggered.connect(self.generateMultiColorView) + self.actionWhite.triggered.connect(lambda: self.scatterViewer.setBackground("w")) + self.actionBlack.triggered.connect(lambda: self.scatterViewer.setBackground("k")) + + with pg.BusyCursor(): + + for arr, fitline, clr, fitClr, rname, feqn in zip( + self.scatterPoints, self.fitLine, self.scatterColors, self.fitColors, self.roiNames, self.fitEqns + ): + + sctrPoints = [] + for pt in arr: + sctrPoints.append( + {"pos": (pt[0], pt[1]), "data": "id", "size": 3, "pen": pg.mkPen(None), "brush": clr} + ) + + # generate a scatter plot item + self.scattered = pg.ScatterPlotItem(size=4.5, pen=clr, brush=pg.mkBrush(5, 214, 255, 200)) + # set scatter plot data + self.scattered.addPoints(sctrPoints, name=rname) + + # set z value negative to show scatter data behind the fit line + self.scattered.setZValue(-10) + + # add scatter plot to the canvas + self.canvas.addItem(self.scattered) + + # generate plotitem for fit line + self.fitLinePlot = pg.PlotDataItem(pen=pg.mkPen(fitClr, width=4.5)) + + # set line plot data + self.fitLinePlot.setData(fitline, name=feqn) + + # add line plot to the canvas + self.canvas.addItem(self.fitLinePlot) + + def generateMultiColorView(self): + self.multichanneldict = {} + + for n, (colorName, image, rname) in enumerate(zip(cmap_dict.keys(), self.maskImages, self.roiNames)): + low, high = np.min(image), np.max(image) + self.multichanneldict[rname] = { + "ImageName": rname, + "ImageDir": ".", + "Image": image, + "Color": colorName, + "CmapLimits": (low, high), + "Opacity": 1.0, + } + + # print( self.multichanneldict) + self.muli_color_window = MultiChannelWindow(image_dict=self.multichanneldict) + self.muli_color_window.show() + + def exportData(self): + + exporter = pg.exporters.CSVExporter(self.canvas) + # exporter.parameters()['columnMode'] = '(x,y,y,y) for all plots' + file_name = QFileDialog().getSaveFileName(self, + "Save CSV Data", + os.path.join(self.user_wd,"scatter.csv"), + "image file (*csv)") + if file_name[0]: + exporter.export(str(file_name[0])) + self.statusbar.showMessage(f"Data saved to {str(file_name[0])}") + self.user_wd = os.path.dirname(file_name[0]) + else: + pass + + def exportAsPNG(self): + file_name = QFileDialog().getSaveFileName(self, + "Save Image", + os.path.join(self.user_wd,"image.png"), + "PNG(*.png);; TIFF(*.tiff);; JPG(*.jpg)" + ) + exporter = pg.exporters.ImageExporter(self.canvas) + + if file_name[0]: + exporter.export(str(file_name[0])) + self.statusbar.showMessage(f"Image saved to {str(file_name[0])}") + self.user_wd = os.path.dirname(file_name[0]) + else: + pass + +class MaskSpecViewer(QtWidgets.QMainWindow): + def __init__(self, xanes_stack=None, xrf_map=None, energy=[]): + super(MaskSpecViewer, self).__init__() + uic.loadUi(os.path.join(ui_path, "uis/MaskedView.ui"), self) + self.user_wd = os.path.abspath("~") + + self.xanes_stack = xanes_stack + self.xrf_map = xrf_map + self.energy = energy + self.xrf_map = self.xanes_stack[-1] + self.view_data() + + # connections + self.sldr_xrf_low.valueChanged.connect(self.create_mask) + self.sldr_xrf_high.valueChanged.connect(self.create_mask) + self.pb_apply_mask.clicked.connect(self.apply_mask_to_xanes) + self.pb_export_mask.clicked.connect(self.export_mask) + self.pb_import_mask.clicked.connect(self.import_a_mask) + self.actionLoad_Energy_List.triggered.connect(self.load_energy) + self.actionLoad_XANES_Stack.triggered.connect(self.load_xanes_stack) + self.actionLoad_XRF_Map.triggered.connect(self.load_xrf_map) + + def view_data(self): + + self.xanes_view.setImage(self.xanes_stack) + self.xanes_view.ui.menuBtn.hide() + self.xanes_view.ui.roiBtn.hide() + (self.dim1, self.dim3, self.dim2) = self.xanes_stack.shape + self.xanes_view.setPredefinedGradient("viridis") + self.xanes_view.setCurrentIndex(self.dim1 // 2) + self.statusbar.showMessage("One image from the XANES stack is used as mask") + self.xrf_view.setImage(self.xrf_map) + self.xrf_view.ui.menuBtn.hide() + self.xrf_view.ui.roiBtn.hide() + self.xrf_view.setPredefinedGradient("viridis") + + self.mask_view.ui.menuBtn.hide() + self.mask_view.ui.roiBtn.hide() + + def create_mask(self): + self.threshold_low = np.around(self.sldr_xrf_low.value() * 0.01, 3) + self.threshold_high = np.around(self.sldr_xrf_high.value() * 0.01, 3) + self.sldr_xrf_low.setMaximum(self.sldr_xrf_high.value() + 1) + self.sldr_xrf_high.setMinimum(self.sldr_xrf_low.value() + 1) + self.norm_xrf_map = remove_nan_inf(self.xrf_map) / remove_nan_inf(self.xrf_map.max()) + self.norm_xrf_map[self.norm_xrf_map < self.threshold_low] = 0 + self.norm_xrf_map[self.norm_xrf_map > self.threshold_high] = 0 + self.xrf_view.setImage(self.norm_xrf_map) + self.le_sldr_vals.setText(str(self.threshold_low) + " to " + str(self.threshold_high)) + self.statusbar.showMessage("New Threshold Applied") + self.xrf_mask = np.where(self.norm_xrf_map > 0, self.norm_xrf_map, 0) + self.xrf_mask[self.xrf_mask > 0] = 1 + self.mask_view.setImage(self.xrf_mask) + + def load_xanes_stack(self): + """loading a new xanes stack""" + filename = QFileDialog().getOpenFileName(self, "Select image data", "", "image file(*tiff *tif )") + self.file_name = str(filename[0]) + self.xanes_stack = tf.imread(self.file_name).transpose(0, 2, 1) + self.view_data() + + def load_energy(self): + """To load energy list that will be used for plotting the spectra. + number of stack should match length of energy list""" + + file_name = QFileDialog().getOpenFileName(self, "Open energy list", "", "text file (*.txt)") + + try: + self.energy = np.loadtxt(str(file_name[0])) + logger.info("Energy file loaded") + assert len(self.energy) == self.dim1 + self.view_data() + + except OSError: + logger.error("No File selected") + pass + + def load_xrf_map(self): + """To xrf map for masking. If 3D mean will be taken""" + + filename = QFileDialog().getOpenFileName(self, "Select image data", "", "image file(*tiff *tif )") + self.xrf_file_name = str(filename[0]) + self.xrf_map = tf.imread(self.xrf_file_name) + if self.xrf_map.ndim == 3: + self.xrf_map = self.xrf_map.mean(0).T + + else: + self.xrf_map = self.xrf_map.T + + assert ( + self.dim3, + self.dim2, + ) == self.xrf_map.shape, f"Unexpected image dimensions: {self.xrf_map.shape} vs {(self.dim2,self.dim3)}" + + self.view_data() + self.create_mask() + + def apply_mask_to_xanes(self): + + """Generates a mask with 0 and 1 from the choosen threshold and multply with the xanes stack. + A spectrum will be generated from the new masked stack""" + + self.masked_xanes = self.xanes_stack * self.xrf_mask + self.xanes_view.setImage(self.masked_xanes) + self.xanes_view.setCurrentIndex(self.dim1 // 2) + self.statusbar.showMessage("Mask Applied to XANES") + self.mask_spec = get_mean_spectra(self.masked_xanes) + + if len(self.energy) != 0: + self.xdata = self.energy + else: + self.xdata = np.arange(0, self.dim1) + self.statusbar.showMessage("No Energy List Available; Integer values are used for plotting") + + self.spectrum_view.plot(self.xdata, self.mask_spec, clear=True) + + def import_a_mask(self): + filename = QFileDialog().getOpenFileName(self, "Select image data", "", "image file(*tiff *tif )") + xrf_file_name = str(filename[0]) + self.xrf_mask = tf.imread(xrf_file_name).T + self.statusbar.showMessage("A New Mask Imported") + self.mask_view.setImage(self.xrf_mask) + self.apply_mask_to_xanes() + + def export_mask(self): + try: + file_name = QFileDialog().getSaveFileName(self, "Save image data", "", "image file(*tiff *tif )") + tf.imsave(str(file_name[0]) + ".tiff", self.xrf_mask.T) + logger.info(f"Updated Image Saved: {str(file_name[0])}") + self.statusbar.showMessage("Mask Exported") + except Exception: + logger.error("No file to save") + pass + +class StackInfo(QtWidgets.QMainWindow): + def __init__(self, text_to_write: str = " "): + super(StackInfo, self).__init__() + uic.loadUi(os.path.join(ui_path, "uis/log.ui"), self) + self.user_wd = os.path.abspath("~") + + self.text_to_write = text_to_write + self.pte_run_cmd.setPlainText(self.text_to_write) + + # connections + self.pb_save_cmd.clicked.connect(self.save_file) + self.pb_clear_cmd.clicked.connect(self.clear_text) + + def save_file(self): + S__File = QFileDialog.getSaveFileName(None, "SaveFile", "/", "txt Files (*.txt)") + + Text = self.pte_run_cmd.toPlainText() + if S__File[0]: + with open(S__File[0], "w") as file: + file.write(Text) + + def clear_text(self): + self.pte_run_cmd.clear() + +class MultiChannelWindow(QtWidgets.QMainWindow): + def __init__(self, image_dict=None): + super(MultiChannelWindow, self).__init__() + if image_dict is None: + image_dict = {} + uic.loadUi(os.path.join(ui_path, "uis/mutlichannel.ui"), self) + self.user_wd = os.path.abspath("~") + + self.canvas = self.img_view.addPlot(title="") + self.canvas.getViewBox().invertY(True) + self.canvas.setAspectLocked(True) + self.cb_choose_color.addItems([i for i in cmap_dict.keys()]) + #self.canvas.set + + self.image_dict = image_dict + self.buildFromDictionary() + + # connections + self.actionLoad.triggered.connect(self.createMuliColorAndList) + self.actionLoad_Stack.triggered.connect(self.createMuliColorAndList) + self.actionSave_Stack_tiff.triggered.connect(self.saveTiffData) + self.cb_choose_color.currentTextChanged.connect(self.updateImageDictionary) + self.pb_update_low_high.clicked.connect(self.updateImageDictionary) + self.listWidget.itemClicked.connect(self.editImageProperties) + self.listWidget.itemDoubleClicked.connect(self.showOneImageOnly) + self.pb_show_selected.clicked.connect(self.showOneImageOnly) + self.pb_show_all.clicked.connect(self.showAllItems) + self.actionLoad_State_File.triggered.connect(self.importState) + self.actionSave_State.triggered.connect(self.exportState) + self.actionSave_View.triggered.connect(self.saveImage) + + def buildFromDictionary(self): + if self.image_dict is not None: + self.createMultiColorView(self.image_dict) + self.displayImageNames(self.image_dict) + else: + pass + + def generateImageDictionary(self): + """Creates a dictionary contains image path, color scheme chosen, throshold limits etc. + when user edits the parameters dictionary will be updated and unwrapped for display later. + This dictionary is saved as json file while saving the state. Two image loading options are possible. + User can either select multiple 2D array images or one 3D array (stack)""" + + clickedAction = self.sender() + + if clickedAction.text() == "Load Images": + # multiple images are selected + self.loadMultipleImageFiles() + + elif clickedAction.text() == "Load Stack": + # an image stack is selected + self.loadAsStack() + + def loadMultipleImageFiles(self): + + filter = "TIFF (*.tiff);;TIF (*.tif)" + QtWidgets.QFileDialog().setFileMode(QtWidgets.QFileDialog.ExistingFiles) + # choose mutliple tiff files + names = QtWidgets.QFileDialog().getOpenFileNames(self, "Open files", " ", filter) + if names[0]: + self.image_dict = {} + # select the file directory. Image files are expected to be in the same folder + self.imageDir = os.path.dirname(names[0][0]) + + # create the dictionary + for colorName, image in zip(cmap_dict.keys(), names[0]): + # squeeze to allow with pseudo 3D axis from some tomo recon (eg. 1, 100,100 array) + im_array = np.squeeze(tf.imread(image)) + # set values for thresholding as image min and max + low, high = np.min(im_array), np.max(im_array) + # name of the tiff file is chosen as key for the dictionary, + # inner keys are properties set for that image + im_name = os.path.basename(image) + # construct the dictionary + self.image_dict[f"{os.path.basename(image)}"] = { + "ImageName": im_name, + "ImageDir": self.imageDir, + "Image": im_array, + "Color": colorName, + "CmapLimits": (low, high), + "Opacity": 1.0, + } + else: + pass + + def loadAsStack(self): + """construct the dictionary with image +number as the key. + All other steps are similar to the loadMultipleImageFiles function""" + + filter = "TIFF (*.tiff);;TIF (*.tif)" + file_name = QtWidgets.QFileDialog().getOpenFileName( + self, "Open a Stack", "", "TIFF(*tiff *tif);;all_files (*)", filter + ) + if file_name[0]: + self.imageDir = os.path.dirname(file_name[0]) + self.image_dict = {} + im_stack = np.squeeze(tf.imread(file_name[0])) + # asset the file is a stack + assert im_stack.ndim == 3, "Not a stack" + # construct the dictionary + for n, (colorName, image) in enumerate(zip(cmap_dict.keys(), im_stack)): + low, high = np.min(image), np.max(image) + self.image_dict[f"Image {n+1}"] = { + "ImageName": f"Image {n+1}", + "ImageDir": self.imageDir, + "Image": image, + "Color": colorName, + "CmapLimits": (low, high), + "Opacity": 1.0, + } + + def loadAnImage(self, image, colormap, cmap_limits, opacity=1): + """load single image and colorbar to the widget. This function will be looped for + multiple images later + """ + # get pg image item + img = pg.ImageItem() + # add image to the graphicsview widget + self.canvas.addItem(img) + # set the color map + cmap = pg.ColorMap(pos=np.linspace(0, 1, len(colormap)), color=colormap) + # image = np.squeeze(tf.imread(image_path)) + # set image to the image item with cmap + img.setImage(np.array(image), lut=cmap.getLookupTable(), opacity=opacity) + + # set colorbar for thresholding + bar = pg.ColorBarItem(values=cmap_limits, cmap=cmap, limits=(0, None), orientation="vertical") + bar.setImageItem(img) + # set composition mode to plus for overlaying + img.setCompositionMode(QtGui.QPainter.CompositionMode_Plus) + + def createMultiColorView(self, image_dictionary): + """Function creates multi color image view by taking image + data and parameters from the dictionary""" + + # clear the plots and list in case of re-loading + self.canvas.clear() + self.listWidget.clear() + + # display individual images in for loop + for path_and_color in image_dictionary.values(): + self.loadAnImage( + path_and_color["Image"], + cmap_dict[path_and_color["Color"]], + path_and_color["CmapLimits"], + path_and_color["Opacity"], + ) + + def showOneImageOnly(self): + editItem = self.listWidget.currentItem() + editRow = self.listWidget.currentRow() + for i in range(self.listWidget.count()): + if self.listWidget.item(i) == editItem: + editItemName = self.listWidget.item(i).text().split(",")[0] + self.image_dict[editItemName]["Opacity"] = 1 + + elif self.listWidget.item(i) != editItem: + editItemName = self.listWidget.item(i).text().split(",")[0] + self.image_dict[editItemName]["Opacity"] = 0 + + self.createMultiColorView(self.image_dict) + self.displayImageNames(self.image_dict) + self.listWidget.setCurrentRow(editRow) + + def showAllItems(self): + + editItem = self.listWidget.currentItem() + editRow = self.listWidget.currentRow() + for i in range(self.listWidget.count()): + editItemName = self.listWidget.item(i).text().split(",")[0] + self.image_dict[editItemName]["Opacity"] = 1 + + self.createMultiColorView(self.image_dict) + self.displayImageNames(self.image_dict) + self.listWidget.setCurrentRow(editRow) + + def displayImageNames(self, image_dictionary): + """Populate the list widget table with image name and color used to plot, + using image dictionary input""" + + for im_name, vals in image_dictionary.items(): + self.listWidget.addItem(f"{im_name},{vals['Color']}") + self.listWidget.setCurrentRow(0) + + def createMuliColorAndList(self): + """Finally Load Images and poplulate the list widget from the dictionary""" + with pg.BusyCursor(): # gives the circle showing gui is doing something + self.generateImageDictionary() + if self.image_dict: + self.createMultiColorView(self.image_dict) + self.displayImageNames(self.image_dict) + + else: + pass + + def sliderSetUp(self, im_array): + """Setting the slider min and max from image values""" + + low = (np.min(im_array) / np.max(im_array)) * 100 + self.sldr_low.setMaximum(100) + self.sldr_low.setMinimum(low) + self.sldr_high.setMaximum(100) + self.sldr_high.setMinimum(low) + + def editImageProperties(self, item): + """function to control the assigned properties such as color, + threshold limits, opacity etc of a single image selected using the list widget item""" + + editItem = item.text() + # get the dictionary key from item text + editItemName = editItem.split(",")[0] + editItemColor = editItem.split(",")[1] + im_array = self.image_dict[editItemName]["Image"] + self.sliderSetUp(im_array) + setValLow = (self.image_dict[editItemName]["CmapLimits"][0] * 100) / np.max(im_array) + setValHigh = (self.image_dict[editItemName]["CmapLimits"][1] * 100) / np.max(im_array) + setOpacity = self.image_dict[editItemName]["Opacity"] * 100 + self.sldr_low.setValue(int(setValLow)) + self.sldr_high.setValue(int(setValHigh)) + self.sldr_opacity.setValue(int(setOpacity)) + self.low_high_vals.setText(f"low:{self.sldr_low.value()}," f"high:{self.sldr_high.value()}") + self.cb_choose_color.setCurrentText(editItemColor) + + def updateImageDictionary(self): + newColor = self.cb_choose_color.currentText() + editItem = self.listWidget.currentItem().text() + editRow = self.listWidget.currentRow() + editItemName = editItem.split(",")[0] + self.imageDir = self.image_dict[editItemName]["ImageDir"] + im_array = self.image_dict[editItemName]["Image"] + self.sliderSetUp(im_array) + cmap_limits = ( + self.sldr_low.value() * np.max(im_array) / 100, + self.sldr_high.value() * np.max(im_array) / 100, + ) + self.low_high_vals.setText(f"low:{cmap_limits[0]:.3f},high:{cmap_limits[1]:.3f}") + opacity = self.sldr_opacity.value() / 100 + self.opacity_val.setText(str(opacity)) + self.image_dict[editItemName] = { + "ImageName": editItemName, + "ImageDir": self.imageDir, + "Image": im_array, + "Color": newColor, + "CmapLimits": cmap_limits, + "Opacity": opacity, + } + + self.createMultiColorView(self.image_dict) + self.displayImageNames(self.image_dict) + self.listWidget.setCurrentRow(editRow) + + def exportState(self): + + file_name = QtWidgets.QFileDialog().getSaveFileName( + self, "Save Current State", "multicolor_params.json", "json file(*json)" + ) + """ + for val in self.image_dict.values(): + val['CmapLimits'] = json.dumps(str(val['CmapLimits'])) + """ + + if file_name[0]: + + with open(f"{file_name[0]}", "w") as fp: + json.dump(self.image_dict, fp, indent=4, cls=jsonEncoder) + + else: + pass + + def importState(self): + file_name = QtWidgets.QFileDialog().getOpenFileName( + self, "Open a State File", "", "json file(*json);;all_files (*)" + ) + if file_name[0]: + with open(file_name[0], "r") as fp: + self.image_dict = json.load(fp) + + self.createMultiColorView(self.image_dict) + self.displayImageNames(self.image_dict) + else: + pass + + def saveImage(self): + file_name = QtWidgets.QFileDialog().getSaveFileName( + self, "Save Image", "multicolor_image.png", "PNG(*.png);; TIFF(*.tiff);; JPG(*.jpg)" + ) + exporter = pg.exporters.ImageExporter(self.canvas.getViewBox()) + exporter.export(file_name[0]) + + def saveTiffData(self): + file_name = QtWidgets.QFileDialog().getSaveFileName(self, "Save Image", 'stack_image_data.tiff', + 'TIFF(*.tiff)') + saveStack = [image_property['Image'] for image_property in self.image_dict.values()] + print(np.shape(saveStack)) + + if file_name[0]: + tf.imsave(file_name[0], saveStack) + else: + return + +class MultiXANESWindow(MultiChannelWindow): + + def __init__(self, image_dict=None, spec_df=None): + super().__init__(image_dict=None) + + self.image_dict = image_dict + self.spec_df = spec_df + + uic.loadUi(os.path.join(ui_path, 'uis/MultiImageSpectrumView.ui'), self) + self.user_wd = os.path.abspath("~") + # Copy from MultiChannelWindow Start here + self.canvas = self.img_view.addPlot(title="") + self.canvas.getViewBox().invertY(True) + #self.canvas.setZValue(-10) + self.canvas.setAspectLocked(True) + self.cb_choose_color.addItems([i for i in cmap_dict.keys()]) + #self.canvas.getViewBox().setBackgroundColor(pg.mkColor(222,222,222)) + #self.canvas.getViewBox().setOpacity(0.5) + + self.image_dict = image_dict + self.buildFromDictionary() + + self.actionLoad.triggered.connect(self.createMuliColorAndList) + self.actionLoad_Stack.triggered.connect(self.createMuliColorAndList) + self.cb_choose_color.currentTextChanged.connect(self.updateImageDictionary) + self.pb_update_low_high.clicked.connect(self.updateImageDictionary) + self.listWidget.itemClicked.connect(self.editImageProperties) + self.listWidget.itemDoubleClicked.connect(self.showOneImageOnly) + self.pb_show_selected.clicked.connect(self.showOneImageOnly) + self.pb_show_all.clicked.connect(self.showAllItems) + self.actionLoad_State_File.triggered.connect(self.importState) + self.actionSave_State.triggered.connect(self.exportState) + self.actionSave_View.triggered.connect(self.saveImage) + # Copy from MultiChannelWindow End here + self.actionSave_Spectrum_Data.triggered.connect(self.exportDisplayedSpectra) + self.listWidget_Spectrum.itemClicked.connect(self.plotNormSpectrum) + self.pb_apply_xanes_norm.clicked.connect(lambda: self.updateSpecData(plotNorm=True)) + self.createMultiSpectrumLibrary() + + [dsb.valueChanged.connect(lambda: self.updateSpecData()) for dsb in + [self.dsb_norm_Eo, self.dsb_norm_pre1, self.dsb_norm_pre2, self.dsb_norm_post1, + self.dsb_norm_post2, self.sb_norm_order]] + + def createSpectrumPropertyDict(self, specName, xdata, ydata, e0, pre1, pre2, norm1, norm2, normOrder): + SingleSpecProperty = {'Name': specName, + 'Data': (xdata, ydata), + 'NormParams': [e0, pre1, pre2, norm1, norm2, normOrder]} + + return SingleSpecProperty + + def createMultiSpectrumLibrary(self): + self.spec_dict = {} + column_names = self.spec_df.columns + spec_array = self.spec_df.to_numpy() + energy = spec_array[:, 0] + for i in range(self.spec_df.shape[1]): + if i != 0: + specData = spec_array[:, i] + e0_init = energy[np.argmax(np.gradient(specData))] + + pre1, pre2, post1, post2 = xanesNormalization( + energy, + specData, + e0=e0_init, + step=None, + nnorm=1, + nvict=0, + method = "guess" + ) + + self.spec_dict[column_names[i]] = self.createSpectrumPropertyDict(column_names[i], energy, specData, + e0_init, pre1, pre2, post1, post2, 1) + self.nomalizeSpectraAndPlot(self.spec_dict) + self.spectrumDictToListWidget() + + def nomalizeSpectraAndPlot(self, spectrumParamDict): + # print(self.spec_dict) + try: + self.spectrum_view.clear() + except: + pass + self.spectrum_view.setLabel("bottom", "Energy") + self.spectrum_view.setLabel("left", "Intensity") + self.spectrum_view.addLegend() + plt_colors = ['r', 'g', (31, 81, 255), 'c', 'm', 'y', 'w'] + for n, params in enumerate(spectrumParamDict.values()): + e0_ = params['NormParams'][0] + pre1_ = params['NormParams'][1] + pre2_ = params['NormParams'][2] + norm1_ = params['NormParams'][3] + norm2_ = params['NormParams'][4] + normOrder_ = params['NormParams'][5] + + preLine, postLine, self.normData = xanesNormalization( + params['Data'][0], + params['Data'][1], + e0=e0_, + step=None, + nnorm=normOrder_, + nvict=0, + pre1=pre1_, + pre2=pre2_, + norm1=norm1_, + norm2=norm2_ + ) + + # 'NormParams': (e0, pre1, pre2, norm1, norm2, normOrder)} + self.spectrum_view.plot(params['Data'][0], self.normData, + pen=pg.mkPen(plt_colors[n], width=2), + name=f"Norm._{params['Name']}") + + def loadAndPlotSpectrumData(self): + filter = 'txt (*.tiff);;csv (*.csv)' + file_name = QtWidgets.QFileDialog().getOpenFileName(self, "Open a spectrum file", '', + 'txt (*.tiff);;csv (*.csv);;all_files (*)', filter) + + if file_name[0]: + self.spec_df = pd.read_csv(file_name[0], index_col=None) + # print(self.spec_df.head()) + # print(self.spec_df.shape[1]) + self.createMultiSpectrumLibrary() + else: + return + + def spectrumDictToListWidget(self): + for params in self.spec_dict.values(): + # Creates a QListWidgetItem + specItem = QtWidgets.QListWidgetItem() + + # Setting QListWidgetItem Text + specItem.setText(params['Name']) + + # Setting your QListWidgetItem Data + specItem.setData(QtCore.Qt.UserRole, params) + + # Add the new rule to the QListWidget + self.listWidget_Spectrum.addItem(specItem) + + def plotNormSpectrum(self, item): + self.selectedItem = item + self.editItemName = self.selectedItem.text() + self.editItemData = self.selectedItem.data(QtCore.Qt.UserRole) + + e0_ = self.editItemData['NormParams'][0] + pre1_ = self.editItemData['NormParams'][1] + pre2_ = self.editItemData['NormParams'][2] + norm1_ = self.editItemData['NormParams'][3] + norm2_ = self.editItemData['NormParams'][4] + normOrder_ = self.editItemData['NormParams'][5] + + self.dsb_norm_Eo.setValue(e0_) # loop later + self.dsb_norm_pre1.setValue(pre1_) + self.dsb_norm_pre2.setValue(pre2_) + self.dsb_norm_post1.setValue(norm1_) + self.dsb_norm_post2.setValue(norm2_) + self.sb_norm_order.setValue(normOrder_) + + preLine, postLine, normSpec = xanesNormalization( + self.editItemData['Data'][0], + self.editItemData['Data'][1], + e0=e0_, + step=None, + nnorm=normOrder_, + nvict=0, + pre1=pre1_, + pre2=pre2_, + norm1=norm1_, + norm2=norm2_ + ) + + self.spectrum_view.clear() + self.spectrum_view.plot(self.editItemData['Data'][0], self.editItemData['Data'][1], + title=f"Normalization Plot_{self.editItemData['Name']}", + pen=pg.mkPen('y', width=2), name=self.editItemData['Name']) + self.spectrum_view.plot(self.editItemData['Data'][0], preLine, pen=pg.mkPen('c', width=2), name='Pre') + self.spectrum_view.plot(self.editItemData['Data'][0], postLine, pen=pg.mkPen('m', width=2), name='Norm') + + # def updateNormParamaters(self): + + def updateSpecData(self, plotNorm=False): + + # loop later + self.editItemData['NormParams'][0] = self.dsb_norm_Eo.value() + self.editItemData['NormParams'][1] = self.dsb_norm_pre1.value() + self.editItemData['NormParams'][2] = self.dsb_norm_pre2.value() + self.editItemData['NormParams'][3] = self.dsb_norm_post1.value() + self.editItemData['NormParams'][4] = self.dsb_norm_post2.value() + self.editItemData['NormParams'][5] = self.sb_norm_order.value() + + self.spec_dict[self.editItemName] = self.editItemData + self.selectedItem.setData(QtCore.Qt.UserRole, self.editItemData) + if plotNorm: + self.nomalizeSpectraAndPlot(self.spec_dict) + else: + self.plotNormSpectrum(self.selectedItem) + + def exportDisplayedSpectra(self): + exporter = pg.exporters.CSVExporter(self.spectrum_view.plotItem) + exporter.parameters()['columnMode'] = '(x,y,y,y) for all plots' + file_name = QFileDialog().getSaveFileName(self, "save spectra", 'xanes.csv', 'spectra (*csv)') + if file_name[0]: + exporter.export(str(file_name[0])) + else: + self.statusbar_main.showMessage('Saving cancelled') + pass + + +def start_xmidas(): + def formatter(prog): + # Set maximum width such that printed help mostly fits in the RTD theme code block (documentation). + return argparse.RawDescriptionHelpFormatter(prog, max_help_position=20, width=90) + ''' + parser = argparse.ArgumentParser( + description=f"XMidas: v{__version__}", + formatter_class=formatter, + ) + parser.parse_args() + ''' + logger.setLevel(logging.INFO) + formatter = logging.Formatter(fmt="%(asctime)s : %(levelname)s : %(message)s") + stream_handler = logging.StreamHandler() + stream_handler.setFormatter(formatter) + stream_handler.setLevel(logging.INFO) + if logger.hasHandlers(): + logger.handlers.clear() + logger.addHandler(stream_handler) + + if version.parse(PYQT_VERSION_STR) >= version.parse("5.14"): + QApplication.setAttribute(QtCore.Qt.HighDpiScaleFactorRoundingPolicy.PassThrough) + + app = QtWidgets.QApplication(sys.argv) + # app.setAttribute(QtCore.Qt.AA_Use96Dpi) + window = midasWindow() + window.show() + sys.exit(app.exec_()) + + +if __name__ == "__main__": + start_xmidas() diff --git a/xmidas/uis/ClusterView.ui b/xmidas/uis/ClusterView.ui index 6f5fa2e..0fea754 100644 --- a/xmidas/uis/ClusterView.ui +++ b/xmidas/uis/ClusterView.ui @@ -1,266 +1,266 @@ - - - MainWindow - - - - 0 - 0 - 979 - 815 - - - - - 0 - 0 - - - - MainWindow - - - background-color: rgb(240, 240, 240); -font: 10pt "Segoe UI"; - - - - QPushButton { -background-color: rgb(175, 236, 255); -color: rgb(255, 5,0); -} - - - - - - - - - - - - - Composite Map - - - Qt::AlignCenter - - - - - - - Opan as RGBCMY Image - - - - - - - - - - - - - - - - - Nth Cluster - - - Qt::AlignCenter - - - - - - - - - - - - - 0 - 0 - - - - 0 - - - 20 - - - 1 - - - 1 - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - font: 12pt "Segoe UI"; - - - 1/3 - - - - - - - - - - - - - - - 100 - - - 100 - - - - - - - font: 75 12pt "MS Shell Dlg 2"; - - - Cluster Spectrum - - - Qt::AlignCenter - - - - - - - - 0 - 0 - - - - - - - Show all - - - - - - - - - QAbstractScrollArea::AdjustToContents - - - - - - - - - - - - - 0 - 0 - 979 - 23 - - - - - Image - - - - - - - - Spectrum - - - - - - - Options - - - - - - - - - - - Save All Clusters - - - - - Save Current Cluster - - - - - Save Composite Map as Masks - - - - - Save All Cluster Spectra - - - - - Save Current Spectrum - - - - - Save Results as a Folder - - - - - - PlotWidget - QGraphicsView -
pyqtgraph
-
- - ImageView - QGraphicsView -
pyqtgraph
-
-
- - -
+ + + MainWindow + + + + 0 + 0 + 979 + 815 + + + + + 0 + 0 + + + + MainWindow + + + background-color: rgb(240, 240, 240); +font: 10pt "Segoe UI"; + + + + QPushButton { +background-color: rgb(175, 236, 255); +color: rgb(255, 5,0); +} + + + + + + + + + + + + + Composite Map + + + Qt::AlignCenter + + + + + + + Opan as RGBCMY Image + + + + + + + + + + + + + + + + + Nth Cluster + + + Qt::AlignCenter + + + + + + + + + + + + + 0 + 0 + + + + 0 + + + 20 + + + 1 + + + 1 + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + font: 12pt "Segoe UI"; + + + 1/3 + + + + + + + + + + + + + + + 100 + + + 100 + + + + + + + font: 75 12pt "MS Shell Dlg 2"; + + + Cluster Spectrum + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + + + Show all + + + + + + + + + QAbstractScrollArea::AdjustToContents + + + + + + + + + + + + + 0 + 0 + 979 + 23 + + + + + Image + + + + + + + + Spectrum + + + + + + + Options + + + + + + + + + + + Save All Clusters + + + + + Save Current Cluster + + + + + Save Composite Map as Masks + + + + + Save All Cluster Spectra + + + + + Save Current Spectrum + + + + + Save Results as a Folder + + + + + + PlotWidget + QGraphicsView +
pyqtgraph
+
+ + ImageView + QGraphicsView +
pyqtgraph
+
+
+ + +
diff --git a/xmidas/uis/ComponentScatterPlot.ui b/xmidas/uis/ComponentScatterPlot.ui index 2e4519b..ee95575 100644 --- a/xmidas/uis/ComponentScatterPlot.ui +++ b/xmidas/uis/ComponentScatterPlot.ui @@ -1,195 +1,195 @@ - - - ScatterPlot - - - - 0 - 0 - 1034 - 859 - - - - Correlation Plot - - - font: 12pt "MS Shell Dlg 2"; - - - - - - - 10 - - - 10 - - - 10 - - - 10 - - - - - Image - - - Qt::AlignCenter - - - - - - - - - - - - 10 - - - 10 - - - 10 - - - 10 - - - - - Image - - - Qt::AlignCenter - - - - - - - - - - - - - - - Qt::LeftToRight - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - Apply - - - - - - - Scatter View of - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - - - - - Add A Line - - - - - - - Create A Mask - - - - - - - Reset Mask - - - - - - - Apply Mask - - - - - - - - - - - 0 - 0 - 1034 - 30 - - - - - File - - - - - - - - - - Save Plot - - - - - Save Images - - - - - - ImageView - QGraphicsView -
pyqtgraph
-
- - GraphicsLayoutWidget - QGraphicsView -
pyqtgraph
-
-
- - -
+ + + ScatterPlot + + + + 0 + 0 + 1034 + 859 + + + + Correlation Plot + + + font: 12pt "MS Shell Dlg 2"; + + + + + + + 10 + + + 10 + + + 10 + + + 10 + + + + + Image + + + Qt::AlignCenter + + + + + + + + + + + + 10 + + + 10 + + + 10 + + + 10 + + + + + Image + + + Qt::AlignCenter + + + + + + + + + + + + + + + Qt::LeftToRight + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + Apply + + + + + + + Scatter View of + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + + + + + Add A Line + + + + + + + Create A Mask + + + + + + + Reset Mask + + + + + + + Apply Mask + + + + + + + + + + + 0 + 0 + 1034 + 30 + + + + + File + + + + + + + + + + Save Plot + + + + + Save Images + + + + + + ImageView + QGraphicsView +
pyqtgraph
+
+ + GraphicsLayoutWidget + QGraphicsView +
pyqtgraph
+
+
+ + +
diff --git a/xmidas/uis/ComponentView.ui b/xmidas/uis/ComponentView.ui index df74ea3..c7aca47 100644 --- a/xmidas/uis/ComponentView.ui +++ b/xmidas/uis/ComponentView.ui @@ -1,296 +1,303 @@ - - - MainWindow - - - - 0 - 0 - 920 - 858 - - - - MainWindow - - - background-color: rgb(240, 240, 240); - - - - - - - - - - - - - - - - - - - - - Components - - - Qt::AlignCenter - - - - - - - - - - - - - - - 0 - 0 - - - - QSlider::groove:horizontal { -border: 1px solid #bbb; -background: white; -height: 10px; -border-radius: 4px; -} - -QSlider::sub-page:horizontal { -background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #66e, stop: 1 #bbf); -background: qlineargradient(x1: 0, y1: 0.2, x2: 1, y2: 1, - stop: 0 #bbf, stop: 1 #55f); -border: 1px solid #777; -height: 10px; -border-radius: 4px; -} - -QSlider::add-page:horizontal { -background: #fff; -border: 1px solid #777; -height: 10px; -border-radius: 4px; -} - -QSlider::handle:horizontal { -background: qlineargradient(x1:0, y1:0, x2:1, y2:1, - stop:0 #eee, stop:1 #ccc); -border: 1px solid #777; -width: 13px; -margin-top: -2px; -margin-bottom: -2px; -border-radius: 4px; -} - -QSlider::handle:horizontal:hover { -background: qlineargradient(x1:0, y1:0, x2:1, y2:1, - stop:0 #fff, stop:1 #ddd); -border: 1px solid #444; -border-radius: 2px; -} - -QSlider::sub-page:horizontal:disabled { -background: #bbb; -border-color: #999; -} - -QSlider::add-page:horizontal:disabled { -background: #eee; -border-color: #999; -} - -QSlider::handle:horizontal:disabled { -background: #eee; -border: 1px solid #aaa; -border-radius: 4px; -} - -QPushButton { -background-color: rgb(175, 236, 255); -color: rgb(255, 0, 127); -} - - - 20 - - - 1 - - - 1 - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - font: 12pt "Segoe UI"; - - - 1/3 - - - - - - - - - - - - - - - - Component Masks - - - Qt::AlignCenter - - - - - - - - - - - - - Show All Spectra - - - - - - - Opan as RGBCMY Image - - - - - - - - - - - - - - - - Masked Spectrum - - - Qt::AlignCenter - - - - - - - - - - - - - - - - Component Spectrum - - - Qt::AlignCenter - - - - - - - - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - - - - Open ScatterPlot - - - - - - - - - - - - 0 - 0 - 920 - 26 - - - - - File - - - - - - - - - Save - - - - - - PlotWidget - QGraphicsView -
pyqtgraph
-
- - ImageView - QGraphicsView -
pyqtgraph
-
-
- - -
+ + + MainWindow + + + + 0 + 0 + 920 + 858 + + + + MainWindow + + + background-color: rgb(240, 240, 240); + + + + + + + + + + + + + + + + + + + + + Components + + + Qt::AlignCenter + + + + + + + + + + + + + + + 0 + 0 + + + + QSlider::groove:horizontal { +border: 1px solid #bbb; +background: white; +height: 10px; +border-radius: 4px; +} + +QSlider::sub-page:horizontal { +background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #66e, stop: 1 #bbf); +background: qlineargradient(x1: 0, y1: 0.2, x2: 1, y2: 1, + stop: 0 #bbf, stop: 1 #55f); +border: 1px solid #777; +height: 10px; +border-radius: 4px; +} + +QSlider::add-page:horizontal { +background: #fff; +border: 1px solid #777; +height: 10px; +border-radius: 4px; +} + +QSlider::handle:horizontal { +background: qlineargradient(x1:0, y1:0, x2:1, y2:1, + stop:0 #eee, stop:1 #ccc); +border: 1px solid #777; +width: 13px; +margin-top: -2px; +margin-bottom: -2px; +border-radius: 4px; +} + +QSlider::handle:horizontal:hover { +background: qlineargradient(x1:0, y1:0, x2:1, y2:1, + stop:0 #fff, stop:1 #ddd); +border: 1px solid #444; +border-radius: 2px; +} + +QSlider::sub-page:horizontal:disabled { +background: #bbb; +border-color: #999; +} + +QSlider::add-page:horizontal:disabled { +background: #eee; +border-color: #999; +} + +QSlider::handle:horizontal:disabled { +background: #eee; +border: 1px solid #aaa; +border-radius: 4px; +} + +QPushButton { +background-color: rgb(175, 236, 255); +color: rgb(255, 0, 127); +} + + + 20 + + + 1 + + + 1 + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + font: 12pt "Segoe UI"; + + + 1/3 + + + + + + + + + + + + + + + + Component Masks + + + Qt::AlignCenter + + + + + + + + + + + + + Show All Spectra + + + + + + + Open as RGBCMY Image + + + + + + + + + + + + + + + + Masked Spectrum + + + Qt::AlignCenter + + + + + + + + + + + + + + + + Component Spectrum + + + Qt::AlignCenter + + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + + Open ScatterPlot + + + + + + + Open XANES Norm. View + + + + + + + + + + + + 0 + 0 + 920 + 26 + + + + + File + + + + + + + + + Save + + + + + + PlotWidget + QGraphicsView +
pyqtgraph
+
+ + ImageView + QGraphicsView +
pyqtgraph
+
+
+ + +
diff --git a/xmidas/uis/Log.ui b/xmidas/uis/Log.ui index 92ca4ec..6ff023b 100644 --- a/xmidas/uis/Log.ui +++ b/xmidas/uis/Log.ui @@ -1,68 +1,68 @@ - - - MainWindow - - - - 0 - 0 - 624 - 759 - - - - Log - - - - - - - font: 10pt "Segoe UI"; - - - QAbstractScrollArea::AdjustToContentsOnFirstShow - - - QPlainTextEdit::NoWrap - - - start typing here.. - - - - - - - - - Save - - - - - - - Clear - - - - - - - - - - - 0 - 0 - 624 - 26 - - - - - - - - + + + MainWindow + + + + 0 + 0 + 624 + 759 + + + + Log + + + + + + + font: 10pt "Segoe UI"; + + + QAbstractScrollArea::AdjustToContentsOnFirstShow + + + QPlainTextEdit::NoWrap + + + start typing here.. + + + + + + + + + Save + + + + + + + Clear + + + + + + + + + + + 0 + 0 + 624 + 26 + + + + + + + + diff --git a/xmidas/uis/MaskedView.ui b/xmidas/uis/MaskedView.ui index a8c40db..7a08678 100644 --- a/xmidas/uis/MaskedView.ui +++ b/xmidas/uis/MaskedView.ui @@ -1,352 +1,352 @@ - - - MainWindow - - - - 0 - 0 - 930 - 779 - - - - MaskedXANES - - - - - - - - - - - - color: rgb(255, 0, 0); -font: 12pt "MS Shell Dlg 2"; - - - XANES Stack - - - Qt::AlignCenter - - - - - - - - - - - - - - font: 12pt "MS Shell Dlg 2"; -color: rgb(255, 0, 0); - - - Mask - - - Qt::AlignCenter - - - - - - - - - - - - 0 - - - - - A Tiff Mask with same dimensions - - - background-color: rgb(170, 255, 255); -font: 10pt "MS Shell Dlg 2"; - - - Import Mask - - - - - - - Save Mask tiff - - - background-color: rgb(170, 255, 255); -font: 10pt "MS Shell Dlg 2"; - - - Export Mask - - - - - - - Apply the mask to XANES - - - background-color: rgb(170, 255, 255); -font: 10pt "MS Shell Dlg 2"; - - - Apply - - - - - - - - - - - font: 75 12pt "MS Shell Dlg 2"; -color: rgb(255, 0, 0); - - - Mean Spectrum - - - Qt::AlignCenter - - - - - - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - font: 12pt "MS Shell Dlg 2"; -color: rgb(255, 0, 0); - - - XRF Map - - - Qt::AlignCenter - - - - - - - font: 10pt "MS Shell Dlg 2"; -color: rgb(255, 0, 0); - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - - - - - - - Qt::NoFocus - - - 100 - - - 5 - - - 5 - - - 0 - - - 0 - - - true - - - Qt::Vertical - - - QSlider::TicksAbove - - - 5 - - - - - - - - - - - Qt::NoFocus - - - 100 - - - 5 - - - 5 - - - 100 - - - 100 - - - true - - - Qt::Vertical - - - false - - - false - - - QSlider::TicksBelow - - - 5 - - - - - - - - - - - - - - - - - 0 - 0 - 930 - 26 - - - - - File - - - - - - - - - - - - - - Save XANES Stack - - - - - Save XRF Map - - - - - Save Spectrum - - - - - Load Energy List - - - - - Load XRF Map - - - - - Load XANES Stack - - - - - - ImageView - QGraphicsView -
pyqtgraph
-
- - PlotWidget - QGraphicsView -
pyqtgraph
-
-
- - -
+ + + MainWindow + + + + 0 + 0 + 930 + 779 + + + + MaskedXANES + + + + + + + + + + + + color: rgb(255, 0, 0); +font: 12pt "MS Shell Dlg 2"; + + + XANES Stack + + + Qt::AlignCenter + + + + + + + + + + + + + + font: 12pt "MS Shell Dlg 2"; +color: rgb(255, 0, 0); + + + Mask + + + Qt::AlignCenter + + + + + + + + + + + + 0 + + + + + A Tiff Mask with same dimensions + + + background-color: rgb(170, 255, 255); +font: 10pt "MS Shell Dlg 2"; + + + Import Mask + + + + + + + Save Mask tiff + + + background-color: rgb(170, 255, 255); +font: 10pt "MS Shell Dlg 2"; + + + Export Mask + + + + + + + Apply the mask to XANES + + + background-color: rgb(170, 255, 255); +font: 10pt "MS Shell Dlg 2"; + + + Apply + + + + + + + + + + + font: 75 12pt "MS Shell Dlg 2"; +color: rgb(255, 0, 0); + + + Mean Spectrum + + + Qt::AlignCenter + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + font: 12pt "MS Shell Dlg 2"; +color: rgb(255, 0, 0); + + + XRF Map + + + Qt::AlignCenter + + + + + + + font: 10pt "MS Shell Dlg 2"; +color: rgb(255, 0, 0); + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + + + + + Qt::NoFocus + + + 100 + + + 5 + + + 5 + + + 0 + + + 0 + + + true + + + Qt::Vertical + + + QSlider::TicksAbove + + + 5 + + + + + + + + + + + Qt::NoFocus + + + 100 + + + 5 + + + 5 + + + 100 + + + 100 + + + true + + + Qt::Vertical + + + false + + + false + + + QSlider::TicksBelow + + + 5 + + + + + + + + + + + + + + + + + 0 + 0 + 930 + 26 + + + + + File + + + + + + + + + + + + + + Save XANES Stack + + + + + Save XRF Map + + + + + Save Spectrum + + + + + Load Energy List + + + + + Load XRF Map + + + + + Load XANES Stack + + + + + + ImageView + QGraphicsView +
pyqtgraph
+
+ + PlotWidget + QGraphicsView +
pyqtgraph
+
+
+ + +
diff --git a/xmidas/uis/MultiImageSpectrumView.ui b/xmidas/uis/MultiImageSpectrumView.ui new file mode 100644 index 0000000..a418c19 --- /dev/null +++ b/xmidas/uis/MultiImageSpectrumView.ui @@ -0,0 +1,734 @@ + + + MainWindow + + + + 0 + 0 + 1182 + 821 + + + + Chemical Map Spectrum View + + + font: 12pt "Segoe UI" + + + + QPushButton { +border: 1px solid #555; +border-radius: 5px; +background: qradialgradient(cx: 0.3, cy: -0.1, +fx: 0.7, fy: 0.1, +radius: 1, stop: 0 #fff, stop: 1 #888); +background-color: rgb(170, 255, 255); +} + +QPushButton:hover{ + background-color: rgb(255, 255, 0); + } + +QPushButton:pressed{ + background-color: rgb(0,255, 0); + } + +font: 12pt "Segoe UI"; + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 0 + 0 + + + + + + + + Edit + + + + + + + 0 + 0 + + + + 0 + + + + + 0 + 0 + 212 + 85 + + + + Thresholding + + + + + + + + + 0 + 0 + + + + 0,100 + + + Qt::AlignCenter + + + + + + + + + + + + 0 + 0 + + + + Qt::NoFocus + + + click update after making changes + + + 100 + + + 5 + + + 5 + + + 100 + + + 100 + + + true + + + Qt::Horizontal + + + false + + + false + + + QSlider::NoTicks + + + 5 + + + + + + + + + + + + 0 + 0 + + + + Qt::NoFocus + + + click update after making changes + + + 100 + + + 5 + + + 5 + + + 0 + + + 0 + + + true + + + Qt::Horizontal + + + QSlider::NoTicks + + + 5 + + + + + + + + + + + + + + + 0 + 0 + 212 + 85 + + + + Opacity + + + + + + + + + 0 + 0 + + + + 1 + + + Qt::AlignCenter + + + + + + + click update after making changes + + + 100 + + + 10 + + + 100 + + + Qt::Horizontal + + + + + + + + + + + + + execute above changes to the selected item + + + Update + + + + + + + + + Show Selected + + + + + + + Show All + + + + + + + + + + 0 + 0 + + + + change properties of the selected item + + + font: 8pt "Segoe UI"; + + + QAbstractScrollArea::AdjustToContents + + + + + + + + + + 0 + 0 + + + + Change Selected To + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + true + + + + + + + + + + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + + XANES Normalization Parameters + + + + + + + 0 + 0 + + + + eV + + + -500.000000000000000 + + + 500.000000000000000 + + + -10.000000000000000 + + + + + + + + 0 + 0 + + + + eV + + + 0.000000000000000 + + + 1000.000000000000000 + + + 25.000000000000000 + + + + + + + + 0 + 0 + + + + 1 + + + 5 + + + + + + + + 0 + 0 + + + + Post-edge + + + + + + + + 0 + 0 + + + + to + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + Norm. Order + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + eV + + + 2 + + + -500.000000000000000 + + + 500.000000000000000 + + + 1.000000000000000 + + + -50.000000000000000 + + + + + + + + 0 + 0 + + + + eV + + + 0.000000000000000 + + + 1500.000000000000000 + + + 75.000000000000000 + + + + + + + + 0 + 0 + + + + Pre-edge + + + + + + + + 0 + 0 + + + + to + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + Eo + + + + + + + + 0 + 0 + + + + eV + + + 1000.000000000000000 + + + 20000.000000000000000 + + + 7125.000000000000000 + + + + + + + + 0 + 0 + + + + Plot Normalized Spectra + + + + + + + + + + + + + + + + + + 0 + 0 + 1182 + 27 + + + + + Image + + + true + + + + + + + + + + Spectrum + + + + + + + + + + Load Images + + + Select and load multiple tiff images to create a multi color view + + + + + Load 2 + + + + + Load 3 + + + + + Load 4 + + + + + Load 5 + + + + + Load 6 + + + + + Export Image + + + Export the image view as a sinle image file + + + + + Save State File + + + Save the current state of the view. Images and properties are saved + + + + + Load State File + + + Load a state (json file) saved previously. + + + + + Load Stack + + + Load images as a stack of tiff + + + + + Save Displayed Spectra(.CSV) + + + + + + PlotWidget + QGraphicsView +
pyqtgraph
+
+ + GraphicsLayoutWidget + QGraphicsView +
pyqtgraph
+
+
+ + +
diff --git a/xmidas/uis/RefChooser.ui b/xmidas/uis/RefChooser.ui index eea416f..26f9474 100644 --- a/xmidas/uis/RefChooser.ui +++ b/xmidas/uis/RefChooser.ui @@ -1,247 +1,252 @@ - - - MainWindow - - - - 0 - 0 - 986 - 985 - - - - ArrowCursor - - - Select References - - - font: 10pt "MS Shell Dlg 2"; - - - - QCheckBox:checked { -color: rgb(255, 75, 52); - background-color: rgb(255, 248, 149); -} - -QPushButton { -background-color: rgb(175, 236, 255); -color: rgb(255, 5,0); -} - -QPushButton:disabled { -background-color: rgb(255, 227, 213); -color: rgb(255, 255, 255); -} - - - - - - - 25 - - - 25 - - - 25 - - - 25 - - - - - - 0 - 0 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - - - Drag the line or double click on a point to select the parameters - - - - - - - - 0 - 0 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - - - - - Max. No. of Refs - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - 1 - - - 10 - - - 2 - - - - - - - - - - - Try All Cobinations - - - - - - - false - - - Fit With Selected - - - - - - - - - - color: rgb(255, 75, 52); -font: 10pt "MS Shell Dlg 2"; - - - N Combinations - - - Qt::AlignCenter - - - - - - - selection-color: rgb(0, 170, 127); - - - 1 - - - - - - - - - Sort by - - - - - - - - Fit Number - - - - - Reduced Chi-Square - - - - - R-Factor - - - - - R-Square - - - - - Chi-Square - - - - - - - - - - - - - - - 0 - 0 - 986 - 22 - - - - - File - - - - - - - - - Export Results (.csv) - - - - - - PlotWidget - QGraphicsView -
pyqtgraph
-
-
- - -
+ + + MainWindow + + + + 0 + 0 + 986 + 985 + + + + ArrowCursor + + + Select References + + + font: 10pt "MS Shell Dlg 2"; + + + + QCheckBox:checked { +color: rgb(255, 75, 52); + background-color: rgb(255, 248, 149); +} + +QPushButton { +background-color: rgb(175, 236, 255); +color: rgb(255, 5,0); +} + +QPushButton:disabled { +background-color: rgb(255, 227, 213); +color: rgb(255, 255, 255); +} + + + + + + + 25 + + + 25 + + + 25 + + + 25 + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + Drag the line or double click on a point to select the parameters + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + + + Max. No. of Refs + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + 1 + + + 10 + + + 2 + + + + + + + + + + + Try All Cobinations + + + + + + + false + + + Fit With Selected + + + + + + + + + + color: rgb(255, 75, 52); +font: 10pt "MS Shell Dlg 2"; + + + N Combinations + + + Qt::AlignCenter + + + + + + + selection-color: rgb(0, 170, 127); + + + 1 + + + + + + + + + Sort by + + + + + + + + Reduced Chi-Square + + + + + Score + + + + + Fit Number + + + + + R-Factor + + + + + R-Square + + + + + Chi-Square + + + + + + + + + + + + + + + 0 + 0 + 986 + 27 + + + + + File + + + + + + + + + Export Results (.csv) + + + + + + PlotWidget + QGraphicsView +
pyqtgraph
+
+
+ + +
diff --git a/xmidas/uis/SVGs/Arrow Down.svg b/xmidas/uis/SVGs/Arrow Down.svg deleted file mode 100644 index cce1eec..0000000 --- a/xmidas/uis/SVGs/Arrow Down.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - diff --git a/xmidas/uis/SVGs/Arrow Left.svg b/xmidas/uis/SVGs/Arrow Left.svg deleted file mode 100644 index 93a3cd4..0000000 --- a/xmidas/uis/SVGs/Arrow Left.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - diff --git a/xmidas/uis/SVGs/Arrow Right.svg b/xmidas/uis/SVGs/Arrow Right.svg deleted file mode 100644 index fb1f0e1..0000000 --- a/xmidas/uis/SVGs/Arrow Right.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - diff --git a/xmidas/uis/SVGs/Arrow Up.svg b/xmidas/uis/SVGs/Arrow Up.svg deleted file mode 100644 index 40dbd57..0000000 --- a/xmidas/uis/SVGs/Arrow Up.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - diff --git a/xmidas/uis/SVGs/Basket.svg b/xmidas/uis/SVGs/Basket.svg deleted file mode 100644 index e33eea3..0000000 --- a/xmidas/uis/SVGs/Basket.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - diff --git a/xmidas/uis/SVGs/Book.svg b/xmidas/uis/SVGs/Book.svg deleted file mode 100644 index 27d79f2..0000000 --- a/xmidas/uis/SVGs/Book.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - diff --git a/xmidas/uis/SVGs/Calendar.svg b/xmidas/uis/SVGs/Calendar.svg deleted file mode 100644 index 8e6ee76..0000000 --- a/xmidas/uis/SVGs/Calendar.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - diff --git a/xmidas/uis/SVGs/Camera.svg b/xmidas/uis/SVGs/Camera.svg deleted file mode 100644 index 80b9e35..0000000 --- a/xmidas/uis/SVGs/Camera.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - diff --git a/xmidas/uis/SVGs/Case.svg b/xmidas/uis/SVGs/Case.svg deleted file mode 100644 index 173d397..0000000 --- a/xmidas/uis/SVGs/Case.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - diff --git a/xmidas/uis/SVGs/Change View.svg b/xmidas/uis/SVGs/Change View.svg deleted file mode 100644 index 9332cfc..0000000 --- a/xmidas/uis/SVGs/Change View.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - diff --git a/xmidas/uis/SVGs/Check V2.svg b/xmidas/uis/SVGs/Check V2.svg deleted file mode 100644 index c8a5e40..0000000 --- a/xmidas/uis/SVGs/Check V2.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - diff --git a/xmidas/uis/SVGs/Check.svg b/xmidas/uis/SVGs/Check.svg deleted file mode 100644 index dadeaed..0000000 --- a/xmidas/uis/SVGs/Check.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - diff --git a/xmidas/uis/SVGs/Chervon Right Circle.svg b/xmidas/uis/SVGs/Chervon Right Circle.svg deleted file mode 100644 index 1492a0b..0000000 --- a/xmidas/uis/SVGs/Chervon Right Circle.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - diff --git a/xmidas/uis/SVGs/Chevron Down Circle.svg b/xmidas/uis/SVGs/Chevron Down Circle.svg deleted file mode 100644 index c7ce17b..0000000 --- a/xmidas/uis/SVGs/Chevron Down Circle.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - diff --git a/xmidas/uis/SVGs/Chevron Down.svg b/xmidas/uis/SVGs/Chevron Down.svg deleted file mode 100644 index 56a3576..0000000 --- a/xmidas/uis/SVGs/Chevron Down.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - diff --git a/xmidas/uis/SVGs/Chevron Left Circle.svg b/xmidas/uis/SVGs/Chevron Left Circle.svg deleted file mode 100644 index a0e84b0..0000000 --- a/xmidas/uis/SVGs/Chevron Left Circle.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - diff --git a/xmidas/uis/SVGs/Chevron Left.svg b/xmidas/uis/SVGs/Chevron Left.svg deleted file mode 100644 index f296cd7..0000000 --- a/xmidas/uis/SVGs/Chevron Left.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - diff --git a/xmidas/uis/SVGs/Chevron Right.svg b/xmidas/uis/SVGs/Chevron Right.svg deleted file mode 100644 index f468a3f..0000000 --- a/xmidas/uis/SVGs/Chevron Right.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - diff --git a/xmidas/uis/SVGs/Chevron Up Circle.svg b/xmidas/uis/SVGs/Chevron Up Circle.svg deleted file mode 100644 index 3ebfe80..0000000 --- a/xmidas/uis/SVGs/Chevron Up Circle.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - diff --git a/xmidas/uis/SVGs/Chevron Up.svg b/xmidas/uis/SVGs/Chevron Up.svg deleted file mode 100644 index fc00cf6..0000000 --- a/xmidas/uis/SVGs/Chevron Up.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - diff --git a/xmidas/uis/SVGs/Clock.svg b/xmidas/uis/SVGs/Clock.svg deleted file mode 100644 index 2bc6f3f..0000000 --- a/xmidas/uis/SVGs/Clock.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - diff --git a/xmidas/uis/SVGs/Close.svg b/xmidas/uis/SVGs/Close.svg deleted file mode 100644 index 5e22958..0000000 --- a/xmidas/uis/SVGs/Close.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - diff --git a/xmidas/uis/SVGs/Cog.svg b/xmidas/uis/SVGs/Cog.svg deleted file mode 100644 index 9a39f32..0000000 --- a/xmidas/uis/SVGs/Cog.svg +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - diff --git a/xmidas/uis/SVGs/Cross Circle.svg b/xmidas/uis/SVGs/Cross Circle.svg deleted file mode 100644 index b4d5ebb..0000000 --- a/xmidas/uis/SVGs/Cross Circle.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - diff --git a/xmidas/uis/SVGs/Download.svg b/xmidas/uis/SVGs/Download.svg deleted file mode 100644 index deeac83..0000000 --- a/xmidas/uis/SVGs/Download.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - diff --git a/xmidas/uis/SVGs/Edit V2.svg b/xmidas/uis/SVGs/Edit V2.svg deleted file mode 100644 index 49c8796..0000000 --- a/xmidas/uis/SVGs/Edit V2.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - diff --git a/xmidas/uis/SVGs/Edit.svg b/xmidas/uis/SVGs/Edit.svg deleted file mode 100644 index 7494aef..0000000 --- a/xmidas/uis/SVGs/Edit.svg +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - diff --git a/xmidas/uis/SVGs/File.svg b/xmidas/uis/SVGs/File.svg deleted file mode 100644 index 9dadb9b..0000000 --- a/xmidas/uis/SVGs/File.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - diff --git a/xmidas/uis/SVGs/Grid.svg b/xmidas/uis/SVGs/Grid.svg deleted file mode 100644 index b9792e5..0000000 --- a/xmidas/uis/SVGs/Grid.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - diff --git a/xmidas/uis/SVGs/Heart.svg b/xmidas/uis/SVGs/Heart.svg deleted file mode 100644 index 5f71778..0000000 --- a/xmidas/uis/SVGs/Heart.svg +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - diff --git a/xmidas/uis/SVGs/Image.svg b/xmidas/uis/SVGs/Image.svg deleted file mode 100644 index 8399140..0000000 --- a/xmidas/uis/SVGs/Image.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - diff --git a/xmidas/uis/SVGs/Link.svg b/xmidas/uis/SVGs/Link.svg deleted file mode 100644 index fba631d..0000000 --- a/xmidas/uis/SVGs/Link.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - diff --git a/xmidas/uis/SVGs/Location Cursor.svg b/xmidas/uis/SVGs/Location Cursor.svg deleted file mode 100644 index 65858b3..0000000 --- a/xmidas/uis/SVGs/Location Cursor.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - diff --git a/xmidas/uis/SVGs/Location.svg b/xmidas/uis/SVGs/Location.svg deleted file mode 100644 index 7626878..0000000 --- a/xmidas/uis/SVGs/Location.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - diff --git a/xmidas/uis/SVGs/Logout.svg b/xmidas/uis/SVGs/Logout.svg deleted file mode 100644 index 04d2dde..0000000 --- a/xmidas/uis/SVGs/Logout.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - diff --git a/xmidas/uis/SVGs/Mail.svg b/xmidas/uis/SVGs/Mail.svg deleted file mode 100644 index c64f596..0000000 --- a/xmidas/uis/SVGs/Mail.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - diff --git a/xmidas/uis/SVGs/Menu.svg b/xmidas/uis/SVGs/Menu.svg deleted file mode 100644 index af3578c..0000000 --- a/xmidas/uis/SVGs/Menu.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/xmidas/uis/SVGs/Message.svg b/xmidas/uis/SVGs/Message.svg deleted file mode 100644 index 0dc9f24..0000000 --- a/xmidas/uis/SVGs/Message.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - diff --git a/xmidas/uis/SVGs/Microphone.svg b/xmidas/uis/SVGs/Microphone.svg deleted file mode 100644 index d3bdff4..0000000 --- a/xmidas/uis/SVGs/Microphone.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - diff --git a/xmidas/uis/SVGs/More.svg b/xmidas/uis/SVGs/More.svg deleted file mode 100644 index ad2af87..0000000 --- a/xmidas/uis/SVGs/More.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - diff --git a/xmidas/uis/SVGs/Plus Circle.svg b/xmidas/uis/SVGs/Plus Circle.svg deleted file mode 100644 index be293d9..0000000 --- a/xmidas/uis/SVGs/Plus Circle.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - diff --git a/xmidas/uis/SVGs/Plus.svg b/xmidas/uis/SVGs/Plus.svg deleted file mode 100644 index 9a2a645..0000000 --- a/xmidas/uis/SVGs/Plus.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - diff --git a/xmidas/uis/SVGs/Power Button.svg b/xmidas/uis/SVGs/Power Button.svg deleted file mode 100644 index 2541ea6..0000000 --- a/xmidas/uis/SVGs/Power Button.svg +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - diff --git a/xmidas/uis/SVGs/Printer.svg b/xmidas/uis/SVGs/Printer.svg deleted file mode 100644 index 5ac0097..0000000 --- a/xmidas/uis/SVGs/Printer.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - diff --git a/xmidas/uis/SVGs/Settings.svg b/xmidas/uis/SVGs/Settings.svg deleted file mode 100644 index 28474d0..0000000 --- a/xmidas/uis/SVGs/Settings.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - diff --git a/xmidas/uis/SVGs/Share.svg b/xmidas/uis/SVGs/Share.svg deleted file mode 100644 index 3fe3197..0000000 --- a/xmidas/uis/SVGs/Share.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - diff --git a/xmidas/uis/SVGs/Shutter.svg b/xmidas/uis/SVGs/Shutter.svg deleted file mode 100644 index 92d99e7..0000000 --- a/xmidas/uis/SVGs/Shutter.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - diff --git a/xmidas/uis/SVGs/Star.svg b/xmidas/uis/SVGs/Star.svg deleted file mode 100644 index facd65d..0000000 --- a/xmidas/uis/SVGs/Star.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - diff --git a/xmidas/uis/SVGs/Trash.svg b/xmidas/uis/SVGs/Trash.svg deleted file mode 100644 index ec862c9..0000000 --- a/xmidas/uis/SVGs/Trash.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - diff --git a/xmidas/uis/SVGs/Trophy.svg b/xmidas/uis/SVGs/Trophy.svg deleted file mode 100644 index 050c08d..0000000 --- a/xmidas/uis/SVGs/Trophy.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - diff --git a/xmidas/uis/SVGs/User.svg b/xmidas/uis/SVGs/User.svg deleted file mode 100644 index 71bb60b..0000000 --- a/xmidas/uis/SVGs/User.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - diff --git a/xmidas/uis/SVGs/Video.svg b/xmidas/uis/SVGs/Video.svg deleted file mode 100644 index 6d8869f..0000000 --- a/xmidas/uis/SVGs/Video.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - diff --git a/xmidas/uis/Scatter3D.ui b/xmidas/uis/Scatter3D.ui index ca2ab8d..cefddb8 100644 --- a/xmidas/uis/Scatter3D.ui +++ b/xmidas/uis/Scatter3D.ui @@ -1,130 +1,130 @@ - - - MainWindow - - - - 0 - 0 - 658 - 628 - - - - MainWindow - - - - - - - - - - - - - - 0 - 0 - - - - - - - - - - - - - 0 - 0 - 658 - 21 - - - - - View - - - - Plot Background - - - - - - - - - - - File - - - - - - - - - - - - Export - - - - - Save as PNG - - - - - Generate MultiColor Mask - - - - - Black - - - - - Charcol - - - - - White - - - - - Open Images - - - - - Open a Stack - - - - - Export Image - - - - - - GLViewWidget - QGraphicsView -
pyqtgraph.opengl
-
-
- - -
+ + + MainWindow + + + + 0 + 0 + 658 + 628 + + + + MainWindow + + + + + + + + + + + + + + 0 + 0 + + + + + + + + + + + + + 0 + 0 + 658 + 21 + + + + + View + + + + Plot Background + + + + + + + + + + + File + + + + + + + + + + + + Export + + + + + Save as PNG + + + + + Generate MultiColor Mask + + + + + Black + + + + + Charcol + + + + + White + + + + + Open Images + + + + + Open a Stack + + + + + Export Image + + + + + + GLViewWidget + QGraphicsView +
pyqtgraph.opengl
+
+
+ + +
diff --git a/xmidas/uis/Scatter3D_plotly.ui b/xmidas/uis/Scatter3D_plotly.ui index 1318496..df22d6d 100644 --- a/xmidas/uis/Scatter3D_plotly.ui +++ b/xmidas/uis/Scatter3D_plotly.ui @@ -1,120 +1,120 @@ - - - MainWindow - - - - 0 - 0 - 856 - 628 - - - - MainWindow - - - - - - - - - - - - - - 0 - 0 - - - - - - - - - - - - - 0 - 0 - 856 - 21 - - - - - View - - - - Plot Background - - - - - - - - - - - - - - toolBar - - - TopToolBarArea - - - false - - - - - - - - Export - - - - - Save as PNG - - - - - Generate MultiColor Mask - - - - - Black - - - - - Grey - - - - - White - - - - - - QWebEngineView - QGraphicsView -
QtWebEngineWidgets
-
-
- - -
+ + + MainWindow + + + + 0 + 0 + 856 + 628 + + + + MainWindow + + + + + + + + + + + + + + 0 + 0 + + + + + + + + + + + + + 0 + 0 + 856 + 21 + + + + + View + + + + Plot Background + + + + + + + + + + + + + + toolBar + + + TopToolBarArea + + + false + + + + + + + + Export + + + + + Save as PNG + + + + + Generate MultiColor Mask + + + + + Black + + + + + Grey + + + + + White + + + + + + QWebEngineView + QGraphicsView +
QtWebEngineWidgets
+
+
+ + +
diff --git a/xmidas/uis/ScatterView.ui b/xmidas/uis/ScatterView.ui index f5394d3..4f0831e 100644 --- a/xmidas/uis/ScatterView.ui +++ b/xmidas/uis/ScatterView.ui @@ -1,249 +1,249 @@ - - - CorrelationPlot - - - - 0 - 0 - 1034 - 859 - - - - Correlation Plot - - - font:10pt "Segoe UI"; - - - - QRadioButton::indicator { - border: 3px solid rgb(150, 150,150); - width: 15px; - height: 15px; - border-radius: 10px; - background: rgb(255, 255, 255); -} -QRadioButton::indicator:hover { - border: 3px solid rgb(58, 66, 81); -} -QRadioButton::indicator:checked { - background: 3px solid rgb(225, 75, 225); - border: 3px solid rgb(255, 252, 255); -} - - - - - - ROI Mask - - - - - - - - color: rgb(255, 0, 0); -background-color: rgb(20, 20, 20); -font: 87 10pt "Segoe UI Black"; - - - - ROI 1 - - - true - - - false - - - - - - - color: rgb(0, 255, 0); -background-color: rgb(20, 20, 20); -font: 87 10pt "Segoe UI Black"; - - - ROI 2 - - - false - - - - - - - color: rgb(0, 255, 255); -background-color: rgb(20, 20, 20); -font: 87 10pt "Segoe UI Black"; - - - ROI 3 - - - false - - - - - - - - - Create Composite Scatter Plot - - - - - - - Apply ROI Mask(s) - - - - - - - Clear Unchecked - - - - - - - Add Checked ROI(s) - - - - - - - - - - 10 - - - 10 - - - 10 - - - 10 - - - - - Image 1 (Blue Shade) - - - Qt::AlignCenter - - - - - - - - - - - - 10 - - - 10 - - - 10 - - - 10 - - - - - Image 2 (Green Shade) - - - Qt::AlignCenter - - - - - - - - - - - - - - - - - 0 - 0 - 1034 - 23 - - - - - File - - - - - - - Plot - - - - - - - - - - - - - Save Plot - - - - - Save Images - - - - - Swap Axes - - - - - - GraphicsLayoutWidget - QGraphicsView -
pyqtgraph
-
- - ImageView - QGraphicsView -
pyqtgraph
-
-
- - -
+ + + CorrelationPlot + + + + 0 + 0 + 1034 + 859 + + + + Correlation Plot + + + font:10pt "Segoe UI"; + + + + QRadioButton::indicator { + border: 3px solid rgb(150, 150,150); + width: 15px; + height: 15px; + border-radius: 10px; + background: rgb(255, 255, 255); +} +QRadioButton::indicator:hover { + border: 3px solid rgb(58, 66, 81); +} +QRadioButton::indicator:checked { + background: 3px solid rgb(225, 75, 225); + border: 3px solid rgb(255, 252, 255); +} + + + + + + ROI Mask + + + + + + + + color: rgb(255, 0, 0); +background-color: rgb(20, 20, 20); +font: 87 10pt "Segoe UI Black"; + + + + ROI 1 + + + true + + + false + + + + + + + color: rgb(0, 255, 0); +background-color: rgb(20, 20, 20); +font: 87 10pt "Segoe UI Black"; + + + ROI 2 + + + false + + + + + + + color: rgb(0, 255, 255); +background-color: rgb(20, 20, 20); +font: 87 10pt "Segoe UI Black"; + + + ROI 3 + + + false + + + + + + + + + Create Composite Scatter Plot + + + + + + + Apply ROI Mask(s) + + + + + + + Clear Unchecked + + + + + + + Add Checked ROI(s) + + + + + + + + + + 10 + + + 10 + + + 10 + + + 10 + + + + + Image 1 (Blue Shade) + + + Qt::AlignCenter + + + + + + + + + + + + 10 + + + 10 + + + 10 + + + 10 + + + + + Image 2 (Green Shade) + + + Qt::AlignCenter + + + + + + + + + + + + + + + + + 0 + 0 + 1034 + 23 + + + + + File + + + + + + + Plot + + + + + + + + + + + + + Save Plot + + + + + Save Images + + + + + Swap Axes + + + + + + GraphicsLayoutWidget + QGraphicsView +
pyqtgraph
+
+ + ImageView + QGraphicsView +
pyqtgraph
+
+
+ + +
diff --git a/xmidas/uis/StackViewer.ui b/xmidas/uis/StackViewer.ui index 62f2665..1d58ea9 100644 --- a/xmidas/uis/StackViewer.ui +++ b/xmidas/uis/StackViewer.ui @@ -1,335 +1,335 @@ - - - MainWindow - - - - 0 - 0 - 931 - 815 - - - - MainWindow - - - - - - 11 - 30 - 701 - 326 - - - - - 0 - 0 - - - - - - - 10 - 390 - 891 - 371 - - - - - - - 720 - 80 - 181 - 171 - - - - - 0 - 0 - - - - Image ROI - - - - 11 - - - 11 - - - - - - - - - - - Y End - - - - - - - true - - - - - - - - - - - X Start - - - - - - - true - - - - - - - - - - - Y Start - - - - - - - true - - - - - - - - - - - X End - - - - - - - true - - - - - - - - - - - - - font: 75 10pt "MS Shell Dlg 2"; - - - Size - - - - - - - true - - - - - - - - - - - - - 720 - 250 - 181 - 139 - - - - - 0 - 0 - - - - Spectrum ROI - - - - - - Sync - - - - - - - - - Start - - - - - - - 10000 - - - 2 - - - - - - - - - - - End - - - - - - - 1 - - - 10000 - - - 2 - - - 10 - - - - - - - - - - - font: 75 10pt "MS Shell Dlg 2"; - - - Size - - - - - - - true - - - - - - - - - - - 730 - 10 - 171 - 70 - - - - - - - false - - - background-color: rgb(170, 255, 255); -font: 75 12pt "MS Shell Dlg 2"; - - - Play - - - - - - - Log View - - - - - - - Reset - - - - - - - - - - 0 - 0 - 931 - 26 - - - - - - - - ImageView - QGraphicsView -
pyqtgraph
-
- - PlotWidget - QGraphicsView -
pyqtgraph
-
-
- - -
+ + + MainWindow + + + + 0 + 0 + 931 + 815 + + + + MainWindow + + + + + + 11 + 30 + 701 + 326 + + + + + 0 + 0 + + + + + + + 10 + 390 + 891 + 371 + + + + + + + 720 + 80 + 181 + 171 + + + + + 0 + 0 + + + + Image ROI + + + + 11 + + + 11 + + + + + + + + + + + Y End + + + + + + + true + + + + + + + + + + + X Start + + + + + + + true + + + + + + + + + + + Y Start + + + + + + + true + + + + + + + + + + + X End + + + + + + + true + + + + + + + + + + + + + font: 75 10pt "MS Shell Dlg 2"; + + + Size + + + + + + + true + + + + + + + + + + + + + 720 + 250 + 181 + 139 + + + + + 0 + 0 + + + + Spectrum ROI + + + + + + Sync + + + + + + + + + Start + + + + + + + 10000 + + + 2 + + + + + + + + + + + End + + + + + + + 1 + + + 10000 + + + 2 + + + 10 + + + + + + + + + + + font: 75 10pt "MS Shell Dlg 2"; + + + Size + + + + + + + true + + + + + + + + + + + 730 + 10 + 171 + 70 + + + + + + + false + + + background-color: rgb(170, 255, 255); +font: 75 12pt "MS Shell Dlg 2"; + + + Play + + + + + + + Log View + + + + + + + Reset + + + + + + + + + + 0 + 0 + 931 + 26 + + + + + + + + ImageView + QGraphicsView +
pyqtgraph
+
+ + PlotWidget + QGraphicsView +
pyqtgraph
+
+
+ + +
diff --git a/xmidas/uis/XANESViewer.ui b/xmidas/uis/XANESViewer.ui index 1209c09..aad2c61 100644 --- a/xmidas/uis/XANESViewer.ui +++ b/xmidas/uis/XANESViewer.ui @@ -1,431 +1,413 @@ - - - mainWindow - - - - 0 - 0 - 1089 - 854 - - - - XANES View - - - font: 10pt "Verdana"; - - - - - - QPushButton { -background-color: rgb(175, 236, 255); -color: rgb(255, 5,0); -} - - - - - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - - - - 0 - 0 - - - - Fit 2D - - - - - - - - - - - E_shift - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - -10.000000000000000 - - - 10.000000000000000 - - - 0.500000000000000 - - - - - - - Alpha: - - - - - - - 10.000000000000000 - - - 0.250000000000000 - - - 0.100000000000000 - - - - - - - - - - - Fitting Model - - - - - - - - NNLS - - - - - LASSO - - - - - RIDGE - - - - - - - - - - - - - - - - - - 0 - 0 - - - - font: 8pt "MS Shell Dlg 2"; -color: rgb(0, 85, 255); - - - Results Text - - - Qt::AlignCenter - - - - - - - - - - - - - - 0 - 0 - - - - ROI Position - - - Qt::AlignCenter - - - - - - - - 0 - 0 - - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - - - - - - - - - - 0 - 0 - - - - - - - - - - - - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - Opan as RGBCMY Image - - - - - - - - 0 - 0 - - - - - - - XANES Maps - - - Qt::AlignCenter - - - - - - - - 0 - 0 - - - - - - - - - 0 - 0 - - - - Show Component Spectrum - - - - - - - - - - - Live ROI Fit - - - Qt::AlignCenter - - - - - - - - - - - - - - - 0 - 0 - - - - Choose References - - - - - - - - - - References - - - Qt::AlignCenter - - - - - - - - - - - - - - - - 0 - 0 - 1089 - 22 - - - - - Image - - - - - - - Spectrum - - - - - - - Fit - - - - - - - - - - - Export Fit Stats - - - - - Export Ref. Plot - - - - - Save Chem Map - - - - - Save R-factor Image - - - - - false - - - Export References - - - - - Export Fit Stats - - - - - Save Live Fit Data - - - - - - PlotWidget - QGraphicsView -
pyqtgraph
-
- - ImageView - QGraphicsView -
pyqtgraph
-
-
- - -
+ + + mainWindow + + + + 0 + 0 + 983 + 811 + + + + XANES Fit View + + + font: 10pt "Segoe UI"; + + + + + QPushButton { +background-color: rgb(175, 236, 255); +color: rgb(255, 5,0); +} + + + + 22 + + + 22 + + + 22 + + + 22 + + + + + + + + + + 0 + 0 + + + + ROI Position + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + + + + + + + + + + 0 + 0 + + + + + + + XANES Maps + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + + + + + + + + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + Open Composite Chem Map Viewer + + + + + + + + + + + Live ROI Fit + + + Qt::AlignCenter + + + + + + + + + + + + + + + 0 + 0 + + + + Choose References + + + + + + + + + + References + + + Qt::AlignCenter + + + + + + + + + + + + + + + + + + + + E_shift + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + -10.000000000000000 + + + 10.000000000000000 + + + 0.500000000000000 + + + + + + + Alpha: + + + + + + + 10.000000000000000 + + + 0.250000000000000 + + + 0.100000000000000 + + + + + + + + + + + Fitting Model + + + + + + + + NNLS + + + + + LASSO + + + + + RIDGE + + + + + + + + + + + + + 0 + 0 + + + + Fit 2D + + + + + + + + 0 + 0 + + + + font: 8pt "MS Shell Dlg 2"; +color: rgb(0, 85, 255); + + + Results Text + + + Qt::AlignCenter + + + + + + + + + + + 0 + 0 + 983 + 23 + + + + + Image + + + + + + + Spectrum + + + + + + + Fit + + + + + + + + + + + Export Fit Stats + + + + + Export Ref. Plot + + + + + Save Chem Map + + + + + Save R-factor Image + + + + + false + + + Export References + + + + + Export Fit Stats + + + + + Save Live Fit Data + + + + + + PlotWidget + QGraphicsView +
pyqtgraph
+
+ + ImageView + QGraphicsView +
pyqtgraph
+
+
+ + +
diff --git a/xmidas/uis/__init__.py b/xmidas/uis/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/xmidas/uis/align_test.ui b/xmidas/uis/align_test.ui index da5604d..a5ab854 100644 --- a/xmidas/uis/align_test.ui +++ b/xmidas/uis/align_test.ui @@ -1,71 +1,71 @@ - - - MainWindow - - - - 0 - 0 - 280 - 271 - - - - MainWindow - - - - - - 100 - 50 - 93 - 28 - - - - Load - - - - - - 100 - 100 - 93 - 28 - - - - Align - - - - - - 100 - 160 - 93 - 28 - - - - Save - - - - - - - 0 - 0 - 280 - 26 - - - - - - - - + + + MainWindow + + + + 0 + 0 + 280 + 271 + + + + MainWindow + + + + + + 100 + 50 + 93 + 28 + + + + Load + + + + + + 100 + 100 + 93 + 28 + + + + Align + + + + + + 100 + 160 + 93 + 28 + + + + Save + + + + + + + 0 + 0 + 280 + 26 + + + + + + + + diff --git a/xmidas/uis/animation2.gif b/xmidas/uis/animation2.gif deleted file mode 100644 index b5261ca..0000000 Binary files a/xmidas/uis/animation2.gif and /dev/null differ diff --git a/xmidas/uis/animationWindow.ui b/xmidas/uis/animationWindow.ui index b90a5e0..21e0c3a 100644 --- a/xmidas/uis/animationWindow.ui +++ b/xmidas/uis/animationWindow.ui @@ -1,68 +1,68 @@ - - - Wait - - - - 0 - 0 - 200 - 200 - - - - Wait... - - - Qt::LeftToRight - - - -background-color: rgba(0, 0, 90, 125); - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - Qt::NoContextMenu - - - - - - - - - false - - - Qt::AlignCenter - - - - - - - - + + + Wait + + + + 0 + 0 + 200 + 200 + + + + Wait... + + + Qt::LeftToRight + + + +background-color: rgba(0, 0, 90, 125); + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + Qt::NoContextMenu + + + + + + + + + false + + + Qt::AlignCenter + + + + + + + + diff --git a/xmidas/uis/mainwindow_admin.ui b/xmidas/uis/mainwindow_admin.ui deleted file mode 100644 index a7d17b9..0000000 --- a/xmidas/uis/mainwindow_admin.ui +++ /dev/null @@ -1,2814 +0,0 @@ - - - Ajith - MainWindow - - - true - - - - 0 - 0 - 1364 - 1060 - - - - - 0 - 0 - - - - NSLS-II MIDAS - - - - ../pancake.ico../pancake.ico - - - font: 12pt "Segoe UI"; - - - - - true - - - - 0 - 0 - - - - font: 10pt "Segoe UI"; - - -QPushButton { -background-color: rgb(175, 236, 255); -color: rgb(255, 5,0); -} - -QSlider::groove:horizontal { -border: 1px solid #bbb; -background: white; -height: 10px; -border-radius: 4px; -} - -QSlider::sub-page:horizontal { -background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #66e, stop: 1 #bbf); -background: qlineargradient(x1: 0, y1: 0.2, x2: 1, y2: 1, - stop: 0 #bbf, stop: 1 #55f); -border: 1px solid #777; -height: 10px; -border-radius: 4px; -} - -QSlider::add-page:horizontal { -background: #fff; -border: 1px solid #777; -height: 10px; -border-radius: 4px; -} - -QSlider::handle:horizontal { -background: qlineargradient(x1:0, y1:0, x2:1, y2:1, - stop:0 #eee, stop:1 #ccc); -border: 1px solid #777; -width: 13px; -margin-top: -2px; -margin-bottom: -2px; -border-radius: 4px; -} - -QSlider::handle:horizontal:hover { -background: qlineargradient(x1:0, y1:0, x2:1, y2:1, - stop:0 #fff, stop:1 #ddd); -border: 1px solid #444; -border-radius: 2px; -} - -QSlider::sub-page:horizontal:disabled { -background: #bbb; -border-color: #999; -} - -QSlider::add-page:horizontal:disabled { -background: #eee; -border-color: #999; -} - -QSlider::handle:horizontal:disabled { -background: #eee; -border: 1px solid #aaa; -border-radius: 4px; -} - - - - - - - - - 0 - 0 - - - - QFrame::StyledPanel - - - QFrame::Sunken - - - - - - - 0 - 0 - - - - - - - 0 - - - - - 0 - 0 - 411 - 355 - - - - Adjust Image Dimensions - - - - - - - 0 - 0 - - - - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - QLayout::SetMinimumSize - - - - - - 0 - 0 - - - - 0 - - - 500 - - - 0 - - - 10 - - - - - - - - 0 - 0 - - - - 1 - - - 5000 - - - 1200 - - - 10 - - - - - - - - 0 - 0 - - - - false - - - px - - - 1 - - - 10000 - - - 100 - - - - - - - - 0 - 0 - - - - false - - - px - - - 1 - - - 10000 - - - 100 - - - - - - - - 0 - 0 - - - - false - - - px - - - 0 - - - 10000 - - - 0 - - - - - - - - 0 - 0 - - - - Y Dimension - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 0 - 0 - - - - false - - - px - - - 0 - - - 10000 - - - 0 - - - - - - - - 0 - 0 - - - - X Dimension - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 0 - 0 - - - - Stack Range - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 0 - 0 - - - - to - - - Qt::AlignCenter - - - - - - - - 0 - 0 - - - - to - - - Qt::AlignCenter - - - - - - - - 0 - 0 - - - - to - - - Qt::AlignCenter - - - - - - - - - - 0 - 0 - - - - Adjust the dimensions of the image - - - - - - Update - - - - - - - - - - - 0 - 0 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - - - - 0 - 0 - - - - Removes one column/row from all four edges - - - Remove Edges - - - - - - - - - true - - - - 0 - 0 - - - - rebin - - - - - - - true - - - - 0 - 0 - - - - Upscale - - - - - - - - - Ratio - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - true - - - - 0 - 0 - - - - font: 10pt "MS Shell Dlg 2"; - - - 2 - - - 16 - - - 2 - - - 2 - - - - - - - - - - - - - - - - - - 0 - 0 - 334 - 423 - - - - Image Processing - - - - - - - - - - - - - - 0 - 0 - - - - Covert image dat to log values. - - - - - - Log - - - false - - - - - - - - 0 - 0 - - - - Intensity Normalization with the last Frame. - - - Normalize - - - - - - - - - - 0 - 0 - - - - Reverse the arrays. 012 will be 210 - - - Qt::LeftToRight - - - Transpose - - - - - - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - - - Remove Outliers (NSigma) - - - - - - - false - - - - - - 200 - - - 3 - - - Qt::Horizontal - - - - - - - 1 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - - - - - Thresholding - - - - - - - - - - - false - - - 100 - - - 5 - - - 5 - - - 5 - - - Qt::Horizontal - - - - - - - 5 - - - - - - - - - - - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - uses savgol_filter to smooth data - - - Smoothen - - - - - - - - - false - - - 3 - - - 12 - - - 2 - - - 2 - - - 3 - - - Qt::Horizontal - - - QSlider::TicksBelow - - - 2 - - - - - - - Window size - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - - - - - - - 0 - 0 - 420 - 344 - - - - Alignment - - - - - - - - - - - Transformations: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - TRANSLATION - - - - - RIGID_BODY - - - - - SCALED_ROTATION - - - - - AFFINE - - - - - BILINEAR - - - - - - - - - - - - Reference: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - previous - - - - - mean - - - - - first - - - - - - - - - - - - - 0 - 0 - - - - Iterative Mode - - - - - - - - - Max Iter. - - - - - - - - 0 - 0 - - - - 2 - - - 24 - - - - - - - - - - - - 0 - 0 - - - - Align - - - - - - - - - - 0 - 0 - - - - Load Reference Stack - - - - - - - No Ref. Available - - - true - - - - - - - - - - 0 - 0 - - - - Save Transformation File - - - - - - - Use - - - - - - - - 0 - 0 - - - - Load Transformation File - - - - - - - - - - - - - - - 0 - 0 - - - - - - - Reset Image - - - - - - - true - - - 2 - - - - - 0 - 0 - 411 - 318 - - - - Component Analysis - - - - - - - - - 0 - 0 - - - - - - - Calculate Components - - - - - - - - 0 - 0 - - - - - - - PCA Scree Plot - - - - - - - - - Method - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 0 - 0 - - - - - PCA - - - - - NMF - - - - - FastICA - - - - - IncrementalPCA - - - - - TruncatedSVD - - - - - FactorAnalysis - - - - - DictionaryLearning - - - - - - - - - - - - Number of Components - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - 1 - - - 4 - - - - - - - - - - - - - 0 - 0 - 411 - 318 - - - - Cluster Analysis - - - - - - - - - 0 - 0 - - - - - - - Calculate Clusters - - - - - - - - 0 - 0 - - - - - - - KMeans Variance Plot - - - - - - - Method - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 0 - 0 - - - - - KMeans - - - - - MiniBatchKMeans - - - - - MeanShift - - - - - Spectral Clustering - - - - - Correlation-Kmeans - - - - - Affinity Propagation - - - - - - - - Number of Clusters - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - 1 - - - 4 - - - - - - - - - - - 0 - 0 - 411 - 318 - - - - XANES Fitting - - - - - - - 11 - - - 11 - - - - - - - - 0 - 0 - - - - - - - Load Energy List - - - - - - - keV - - - - - - - - - - - - 0 - 0 - - - - - - - Load Ref. Spec. - - - - - - - - - - Plot - - - - - - - - - true - - - - 0 - 0 - - - - - - - Fit - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - - - - 0 - 0 - - - - font:12pt "Segoe UI"; - - - - - - - QLayout::SetMaximumSize - - - - - - 0 - 0 - - - - - - - Save Image (.tiff) - - - - - - - 0 - - - - Live - - - - - - - 0 - 0 - - - - Send to Collector - - - - - - - - 0 - 0 - - - - - - - Save - - - - - - - - 0 - 0 - - - - - - - - - Normalized - - - - - - Save - - - - - - - Clear - - - - - - - Norm. to Collector - - - - - - - - 0 - 0 - - - - - - - - - Collector - - - - - - - 0 - 0 - - - - - - - - Clear - - - - - - - Save - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - 0 - 0 - - - - - - - - - 0 - 0 - - - - Stack Info - - - - - - - - - - - 0 - 0 - - - - QFrame::StyledPanel - - - QFrame::Sunken - - - - QLayout::SetDefaultConstraint - - - 11 - - - 11 - - - - - - 0 - 0 - - - - ROI Shape - - - - - - - 0 - 0 - - - - Rectangle - - - true - - - - - - - - 0 - 0 - - - - Ellipse - - - - - - - - 0 - 0 - - - - Polygon - - - false - - - - - - - - 0 - 0 - - - - Circle - - - - - - - - 0 - 0 - - - - Line - - - - - - - - 0 - 0 - - - - Show ROI Stack - - - - - - - - - - font: 9pt "Segoe UI"; - - - ROI Positions - - - - - - Spectrum ROI - - - - - - color: rgb(255, 0,0); - - - roi_size - - - - - - - Range (eV): - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - Size (eV): - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - color: rgb(255, 0,0); - - - roix, roiy - - - - - - - - - - Image ROI - - - - - - Position: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - color: rgb(255, 0,0); - - - roix, roiy - - - - - - - - - - Size(pixels): - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - color: rgb(255, 0, 0); - - - roi_size - - - - - - - - - - - - - - 0 - 0 - - - - 2 - - - - - 0 - 0 - 361 - 193 - - - - Image Calculations - - - - - - - - - - - - - - - Add ROI_2 - - - - - - - Calculation: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 0 - 0 - - - - - Subtract - - - - - Divide - - - - - Add - - - - - Compare - - - - - - - - - - - - - 0 - 0 - 361 - 193 - - - - Spectrum Calculations - - - - - - - - - - - - - 0 - 0 - - - - Add ROI 2 - - - true - - - false - - - - - - - - - - 0 - 0 - - - - Calculation - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 0 - 0 - - - - - Divide - - - - - Subtract - - - - - Add - - - - - Correlation Plot - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - 0 - 0 - 433 - 308 - - - - XANES Normalization - - - - - - - 0 - 0 - - - - to - - - Qt::AlignCenter - - - - - - - - 0 - 0 - - - - 1 - - - 5 - - - - - - - - 0 - 0 - - - - Eo - - - - - - - - 0 - 0 - - - - eV - - - 1000.000000000000000 - - - 20000.000000000000000 - - - 7125.000000000000000 - - - - - - - - 0 - 0 - - - - calculate the energy point with maximum derivative - - - Auto Eo - - - - - - - - 0 - 0 - - - - eV - - - 2 - - - -500.000000000000000 - - - 500.000000000000000 - - - 1.000000000000000 - - - -50.000000000000000 - - - - - - - - 0 - 0 - - - - Pre-edge - - - - - - - - 0 - 0 - - - - eV - - - -500.000000000000000 - - - 500.000000000000000 - - - -10.000000000000000 - - - - - - - - 0 - 0 - - - - Post-edge - - - - - - - - 0 - 0 - - - - to - - - Qt::AlignCenter - - - - - - - - 0 - 0 - - - - Norm. Order - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 0 - 0 - - - - eV - - - 0.000000000000000 - - - 1000.000000000000000 - - - 25.000000000000000 - - - - - - - - 0 - 0 - - - - <html><head/><body><p> For mbak algorithm only, must be in <span style=" font-weight:600; font-style:italic;">element&lt;space&gt;edge</span> format</p></body></html> - - - Fe K - - - - - - - - 0 - 0 - - - - Element: - - - - - - - - 0 - 0 - - - - eV - - - 0.000000000000000 - - - 1500.000000000000000 - - - 75.000000000000000 - - - - - - - Initial Guess - - - - - - - - 0 - 0 - - - - Apply to Spectrum - - - - - - - Apply to Stack - - - - - - - - - - - - - - Available Stacks - - - - - - - - - - - - 0 - 0 - 1364 - 34 - - - - - Help - - - - - - - File - - - - - - - - - - - true - - - Tools - - - - - - - View - - - - Plot Background - - - - - - - - - - Window Background - - - - - - - - Change_Plot_Line_Width - - - - - - - - - - - - - - - - - Spectrum - - - - - - - - - Image - - - - - - - - - - - - - - - - Open PDF - - - - - Open in GitHub (most updated) - - - - - true - - - Open Image Data - - - Support all tiff and specific h5 files - - - Ctrl+O - - - - - Close - - - - - Exit - - - Ctrl+Q - - - - - false - - - Export Tiff Stack - - - Save the displayed/Modified stack as a tiff file - - - Ctrl+S - - - - - Open PyXRF - - - - - Open Image J - - - - - Open TomViz - - - - - Open Mantis - - - - - Open Athena - - - - - DataBroker - - - - - Open HXN DB - - - - - false - - - Load Energy - - - Load list of energies for XANES stack. Supports only .txt fromat - - - Ctrl+E - - - - - Create a Virtual Stack - - - Create a stack from multiple tiff images of same shape - - - Ctrl+M - - - - - true - - - Open Mask Generator - - - A new window will be opened to creat threshold based masks - - - - - Create elist from log file - - - - - false - - - Save Energy List - - - - - true - - - MultiColorView - - - A new window will be opened to align images in a stack - - - - - White - - - - - true - - - true - - - Black - - - - - Red - - - - - Yellow - - - - - Blue - - - - - false - - - false - - - Dark Mode - - - - - Black - - - - - false - - - false - - - Default - - - - - Vivid - - - - - Save Sum Image - - - - - Subtract ROI as Background - - - - - Export Norm. Params - - - - - 2 - - - - - 4 - - - - - 6 - - - - - 10 - - - - - 2 - - - - - 3 - - - - - 4 - - - - - 5 - - - - - 6 - - - - - 8 - - - - - 10 - - - - - 2 - - - - - Import Norm. Params - - - - - 1 - - - - - Save Sum Spectrum - - - - - Save Mean Spectrum - - - - - Save Current Image as Mask - - - - - - PlotWidget - QGraphicsView -
pyqtgraph
-
- - ImageView - QGraphicsView -
pyqtgraph
-
-
- - -
diff --git a/xmidas/uis/maskedScatterPlotFit.ui b/xmidas/uis/maskedScatterPlotFit.ui index e8bd894..8ad2145 100644 --- a/xmidas/uis/maskedScatterPlotFit.ui +++ b/xmidas/uis/maskedScatterPlotFit.ui @@ -1,187 +1,187 @@ - - - CorrelationPlot - - - - 0 - 0 - 967 - 816 - - - - Correlation Plot - - - font: 12pt "MS Shell Dlg 2"; - - - - - 10 - - - 25 - - - 10 - - - 10 - - - - - Scatter Plot - - - - - - - 0 - 0 - - - - - - - - - - - Mask - - - - - - - 0 - 0 - - - - - - - - - - - - 0 - 0 - - - - Fit Results - - - - - - Copy - - - - - - - - 0 - 0 - - - - font: 11pt "Consolas"; - - - true - - - - - - - Export - - - - - - - - - - Masked Image1 - - - - - - - 0 - 0 - - - - - - - - - - - - - - 0 - 0 - 967 - 25 - - - - - Export - - - - - - - - - - Scatter Plot+Fit - - - - - Mask - - - - - Masked Image - - - - - - ImageView - QGraphicsView -
pyqtgraph
-
- - GraphicsLayoutWidget - QGraphicsView -
pyqtgraph
-
-
- - -
+ + + CorrelationPlot + + + + 0 + 0 + 967 + 816 + + + + Correlation Plot + + + font: 12pt "MS Shell Dlg 2"; + + + + + 10 + + + 25 + + + 10 + + + 10 + + + + + Scatter Plot + + + + + + + 0 + 0 + + + + + + + + + + + Mask + + + + + + + 0 + 0 + + + + + + + + + + + + 0 + 0 + + + + Fit Results + + + + + + Copy + + + + + + + + 0 + 0 + + + + font: 11pt "Consolas"; + + + true + + + + + + + Export + + + + + + + + + + Masked Image1 + + + + + + + 0 + 0 + + + + + + + + + + + + + + 0 + 0 + 967 + 25 + + + + + Export + + + + + + + + + + Scatter Plot+Fit + + + + + Mask + + + + + Masked Image + + + + + + ImageView + QGraphicsView +
pyqtgraph
+
+ + GraphicsLayoutWidget + QGraphicsView +
pyqtgraph
+
+
+ + +
diff --git a/xmidas/uis/midas.py b/xmidas/uis/midas.py deleted file mode 100644 index 4b0ea40..0000000 --- a/xmidas/uis/midas.py +++ /dev/null @@ -1,1048 +0,0 @@ -# -*- coding: utf-8 -*- - -# Form implementation generated from reading ui file 'mainwindow_admin.ui' -# -# Created by: PyQt5 UI code generator 5.14.1 -# -# WARNING! All changes made in this file will be lost! - - -from PyQt5 import QtCore, QtGui, QtWidgets - - -class Ui_MainWindow(object): - def setupUi(self, MainWindow): - MainWindow.setObjectName("MainWindow") - MainWindow.setEnabled(True) - MainWindow.resize(1246, 936) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(MainWindow.sizePolicy().hasHeightForWidth()) - MainWindow.setSizePolicy(sizePolicy) - MainWindow.setStyleSheet("font: 10pt \"Segoe UI\";\n" -"\n" -"\n" -"QSlider::groove:horizontal {\n" -"border: 1px solid #bbb;\n" -"background: white;\n" -"height: 10px;\n" -"border-radius: 4px;\n" -"}\n" -"\n" -"QSlider::sub-page:horizontal {\n" -"background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,\n" -" stop: 0 #66e, stop: 1 #bbf);\n" -"background: qlineargradient(x1: 0, y1: 0.2, x2: 1, y2: 1,\n" -" stop: 0 #bbf, stop: 1 #55f);\n" -"border: 1px solid #777;\n" -"height: 10px;\n" -"border-radius: 4px;\n" -"}\n" -"\n" -"QSlider::add-page:horizontal {\n" -"background: #fff;\n" -"border: 1px solid #777;\n" -"height: 10px;\n" -"border-radius: 4px;\n" -"}\n" -"\n" -"QSlider::handle:horizontal {\n" -"background: qlineargradient(x1:0, y1:0, x2:1, y2:1,\n" -" stop:0 #eee, stop:1 #ccc);\n" -"border: 1px solid #777;\n" -"width: 13px;\n" -"margin-top: -2px;\n" -"margin-bottom: -2px;\n" -"border-radius: 4px;\n" -"}\n" -"\n" -"QSlider::handle:horizontal:hover {\n" -"background: qlineargradient(x1:0, y1:0, x2:1, y2:1,\n" -" stop:0 #fff, stop:1 #ddd);\n" -"border: 1px solid #444;\n" -"border-radius: 4px;\n" -"}\n" -"\n" -"QSlider::sub-page:horizontal:disabled {\n" -"background: #bbb;\n" -"border-color: #999;\n" -"}\n" -"\n" -"QSlider::add-page:horizontal:disabled {\n" -"background: #eee;\n" -"border-color: #999;\n" -"}\n" -"\n" -"QSlider::handle:horizontal:disabled {\n" -"background: #eee;\n" -"border: 1px solid #aaa;\n" -"border-radius: 4px;\n" -"}") - self.centralwidget = QtWidgets.QWidget(MainWindow) - self.centralwidget.setEnabled(False) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.MinimumExpanding) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.centralwidget.sizePolicy().hasHeightForWidth()) - self.centralwidget.setSizePolicy(sizePolicy) - self.centralwidget.setStyleSheet("QSlider::groove:horizontal {\n" -"border: 1px solid #bbb;\n" -"background: white;\n" -"height: 10px;\n" -"border-radius: 4px;\n" -"}\n" -"\n" -"QSlider::sub-page:horizontal {\n" -"background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,\n" -" stop: 0 #66e, stop: 1 #bbf);\n" -"background: qlineargradient(x1: 0, y1: 0.2, x2: 1, y2: 1,\n" -" stop: 0 #bbf, stop: 1 #55f);\n" -"border: 1px solid #777;\n" -"height: 10px;\n" -"border-radius: 4px;\n" -"}\n" -"\n" -"QSlider::add-page:horizontal {\n" -"background: #fff;\n" -"border: 1px solid #777;\n" -"height: 10px;\n" -"border-radius: 4px;\n" -"}\n" -"\n" -"QSlider::handle:horizontal {\n" -"background: qlineargradient(x1:0, y1:0, x2:1, y2:1,\n" -" stop:0 #eee, stop:1 #ccc);\n" -"border: 1px solid #777;\n" -"width: 13px;\n" -"margin-top: -2px;\n" -"margin-bottom: -2px;\n" -"border-radius: 4px;\n" -"}\n" -"\n" -"QSlider::handle:horizontal:hover {\n" -"background: qlineargradient(x1:0, y1:0, x2:1, y2:1,\n" -" stop:0 #fff, stop:1 #ddd);\n" -"border: 1px solid #444;\n" -"border-radius: 4px;\n" -"}\n" -"\n" -"QSlider::sub-page:horizontal:disabled {\n" -"background: #bbb;\n" -"border-color: #999;\n" -"}\n" -"\n" -"QSlider::add-page:horizontal:disabled {\n" -"background: #eee;\n" -"border-color: #999;\n" -"}\n" -"\n" -"QSlider::handle:horizontal:disabled {\n" -"background: #eee;\n" -"border: 1px solid #aaa;\n" -"border-radius: 4px;\n" -"}") - self.centralwidget.setObjectName("centralwidget") - self.gridLayout_16 = QtWidgets.QGridLayout(self.centralwidget) - self.gridLayout_16.setObjectName("gridLayout_16") - self.scrollArea = QtWidgets.QScrollArea(self.centralwidget) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Expanding) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.scrollArea.sizePolicy().hasHeightForWidth()) - self.scrollArea.setSizePolicy(sizePolicy) - self.scrollArea.setStyleSheet("font:12pt \"Segoe UI\";") - self.scrollArea.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustIgnored) - self.scrollArea.setWidgetResizable(True) - self.scrollArea.setAlignment(QtCore.Qt.AlignCenter) - self.scrollArea.setObjectName("scrollArea") - self.scrollAreaWidgetContents = QtWidgets.QWidget() - self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 321, 855)) - self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents") - self.gridLayout_9 = QtWidgets.QGridLayout(self.scrollAreaWidgetContents) - self.gridLayout_9.setObjectName("gridLayout_9") - self.gridLayout_13 = QtWidgets.QGridLayout() - self.gridLayout_13.setObjectName("gridLayout_13") - self.pb_reset_img = QtWidgets.QPushButton(self.scrollAreaWidgetContents) - self.pb_reset_img.setStyleSheet("background-color: rgb(175, 236, 255);\n" -"color: rgb(255, 0, 127);\n" -"") - self.pb_reset_img.setObjectName("pb_reset_img") - self.gridLayout_13.addWidget(self.pb_reset_img, 0, 0, 1, 1) - self.gridLayout_9.addLayout(self.gridLayout_13, 1, 0, 1, 1) - self.toolBox = QtWidgets.QToolBox(self.scrollAreaWidgetContents) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.toolBox.sizePolicy().hasHeightForWidth()) - self.toolBox.setSizePolicy(sizePolicy) - self.toolBox.setStyleSheet("font: 10pt \"Segoe UI\";") - self.toolBox.setObjectName("toolBox") - self.page = QtWidgets.QWidget() - self.page.setGeometry(QtCore.QRect(0, 0, 368, 331)) - self.page.setObjectName("page") - self.gridLayout_31 = QtWidgets.QGridLayout(self.page) - self.gridLayout_31.setObjectName("gridLayout_31") - self.frame_7 = QtWidgets.QFrame(self.page) - self.frame_7.setFrameShape(QtWidgets.QFrame.StyledPanel) - self.frame_7.setFrameShadow(QtWidgets.QFrame.Raised) - self.frame_7.setObjectName("frame_7") - self.gridLayout_30 = QtWidgets.QGridLayout(self.frame_7) - self.gridLayout_30.setObjectName("gridLayout_30") - self.gridLayout_17 = QtWidgets.QGridLayout() - self.gridLayout_17.setSizeConstraint(QtWidgets.QLayout.SetMinimumSize) - self.gridLayout_17.setObjectName("gridLayout_17") - self.sb_zrange1 = QtWidgets.QSpinBox(self.frame_7) - self.sb_zrange1.setMinimum(0) - self.sb_zrange1.setMaximum(500) - self.sb_zrange1.setProperty("value", 0) - self.sb_zrange1.setDisplayIntegerBase(10) - self.sb_zrange1.setObjectName("sb_zrange1") - self.gridLayout_17.addWidget(self.sb_zrange1, 0, 1, 1, 1) - self.label_5 = QtWidgets.QLabel(self.frame_7) - self.label_5.setAlignment(QtCore.Qt.AlignCenter) - self.label_5.setObjectName("label_5") - self.gridLayout_17.addWidget(self.label_5, 1, 2, 1, 1) - self.label_9 = QtWidgets.QLabel(self.frame_7) - self.label_9.setAlignment(QtCore.Qt.AlignCenter) - self.label_9.setObjectName("label_9") - self.gridLayout_17.addWidget(self.label_9, 2, 2, 1, 1) - self.sb_yrange2 = QtWidgets.QSpinBox(self.frame_7) - self.sb_yrange2.setReadOnly(False) - self.sb_yrange2.setMinimum(1) - self.sb_yrange2.setMaximum(10000) - self.sb_yrange2.setProperty("value", 100) - self.sb_yrange2.setObjectName("sb_yrange2") - self.gridLayout_17.addWidget(self.sb_yrange2, 2, 3, 1, 1) - self.sb_zrange2 = QtWidgets.QSpinBox(self.frame_7) - self.sb_zrange2.setMinimum(1) - self.sb_zrange2.setMaximum(5000) - self.sb_zrange2.setProperty("value", 1200) - self.sb_zrange2.setDisplayIntegerBase(10) - self.sb_zrange2.setObjectName("sb_zrange2") - self.gridLayout_17.addWidget(self.sb_zrange2, 0, 3, 1, 1) - self.sb_xrange1 = QtWidgets.QSpinBox(self.frame_7) - self.sb_xrange1.setReadOnly(False) - self.sb_xrange1.setMinimum(0) - self.sb_xrange1.setMaximum(10000) - self.sb_xrange1.setProperty("value", 0) - self.sb_xrange1.setObjectName("sb_xrange1") - self.gridLayout_17.addWidget(self.sb_xrange1, 1, 1, 1, 1) - self.sb_xrange2 = QtWidgets.QSpinBox(self.frame_7) - self.sb_xrange2.setReadOnly(False) - self.sb_xrange2.setMinimum(1) - self.sb_xrange2.setMaximum(10000) - self.sb_xrange2.setProperty("value", 100) - self.sb_xrange2.setObjectName("sb_xrange2") - self.gridLayout_17.addWidget(self.sb_xrange2, 1, 3, 1, 1) - self.label_22 = QtWidgets.QLabel(self.frame_7) - self.label_22.setAlignment(QtCore.Qt.AlignCenter) - self.label_22.setObjectName("label_22") - self.gridLayout_17.addWidget(self.label_22, 0, 2, 1, 1) - self.label_19 = QtWidgets.QLabel(self.frame_7) - self.label_19.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.label_19.setObjectName("label_19") - self.gridLayout_17.addWidget(self.label_19, 1, 0, 1, 1) - self.sb_yrange1 = QtWidgets.QSpinBox(self.frame_7) - self.sb_yrange1.setReadOnly(False) - self.sb_yrange1.setMinimum(0) - self.sb_yrange1.setMaximum(10000) - self.sb_yrange1.setProperty("value", 0) - self.sb_yrange1.setObjectName("sb_yrange1") - self.gridLayout_17.addWidget(self.sb_yrange1, 2, 1, 1, 1) - self.label_15 = QtWidgets.QLabel(self.frame_7) - self.label_15.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.label_15.setObjectName("label_15") - self.gridLayout_17.addWidget(self.label_15, 2, 0, 1, 1) - self.label_20 = QtWidgets.QLabel(self.frame_7) - self.label_20.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.label_20.setObjectName("label_20") - self.gridLayout_17.addWidget(self.label_20, 0, 0, 1, 1) - self.gridLayout_30.addLayout(self.gridLayout_17, 0, 0, 1, 1) - self.pb_crop = QtWidgets.QPushButton(self.frame_7) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.pb_crop.sizePolicy().hasHeightForWidth()) - self.pb_crop.setSizePolicy(sizePolicy) - self.pb_crop.setStyleSheet("background-color: rgb(175, 236, 255);\n" -"color: rgb(255, 0, 127);\n" -"") - self.pb_crop.setObjectName("pb_crop") - self.gridLayout_30.addWidget(self.pb_crop, 1, 0, 1, 1) - self.gridLayout_31.addWidget(self.frame_7, 1, 0, 1, 1) - self.frame_6 = QtWidgets.QFrame(self.page) - self.frame_6.setFrameShape(QtWidgets.QFrame.StyledPanel) - self.frame_6.setFrameShadow(QtWidgets.QFrame.Raised) - self.frame_6.setObjectName("frame_6") - self.gridLayout_7 = QtWidgets.QGridLayout(self.frame_6) - self.gridLayout_7.setObjectName("gridLayout_7") - self.horizontalLayout_9 = QtWidgets.QHBoxLayout() - self.horizontalLayout_9.setObjectName("horizontalLayout_9") - self.cb_rebin = QtWidgets.QCheckBox(self.frame_6) - self.cb_rebin.setEnabled(False) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.cb_rebin.sizePolicy().hasHeightForWidth()) - self.cb_rebin.setSizePolicy(sizePolicy) - self.cb_rebin.setObjectName("cb_rebin") - self.horizontalLayout_9.addWidget(self.cb_rebin) - self.horizontalLayout_10 = QtWidgets.QHBoxLayout() - self.horizontalLayout_10.setObjectName("horizontalLayout_10") - self.cb_upscale = QtWidgets.QCheckBox(self.frame_6) - self.cb_upscale.setEnabled(False) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.cb_upscale.sizePolicy().hasHeightForWidth()) - self.cb_upscale.setSizePolicy(sizePolicy) - self.cb_upscale.setObjectName("cb_upscale") - self.horizontalLayout_10.addWidget(self.cb_upscale) - self.horizontalLayout_9.addLayout(self.horizontalLayout_10) - self.sb_scaling_factor = QtWidgets.QSpinBox(self.frame_6) - self.sb_scaling_factor.setEnabled(False) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.sb_scaling_factor.sizePolicy().hasHeightForWidth()) - self.sb_scaling_factor.setSizePolicy(sizePolicy) - self.sb_scaling_factor.setStyleSheet("font: 10pt \"MS Shell Dlg 2\";") - self.sb_scaling_factor.setMinimum(2) - self.sb_scaling_factor.setMaximum(16) - self.sb_scaling_factor.setSingleStep(2) - self.sb_scaling_factor.setProperty("value", 2) - self.sb_scaling_factor.setObjectName("sb_scaling_factor") - self.horizontalLayout_9.addWidget(self.sb_scaling_factor) - self.gridLayout_7.addLayout(self.horizontalLayout_9, 2, 0, 1, 1) - self.cb_remove_edges = QtWidgets.QCheckBox(self.frame_6) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.cb_remove_edges.sizePolicy().hasHeightForWidth()) - self.cb_remove_edges.setSizePolicy(sizePolicy) - self.cb_remove_edges.setObjectName("cb_remove_edges") - self.gridLayout_7.addWidget(self.cb_remove_edges, 0, 0, 1, 1) - self.gridLayout_31.addWidget(self.frame_6, 2, 0, 1, 1) - spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) - self.gridLayout_31.addItem(spacerItem, 0, 0, 1, 1) - spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) - self.gridLayout_31.addItem(spacerItem1, 3, 0, 1, 1) - self.toolBox.addItem(self.page, "") - self.page_2 = QtWidgets.QWidget() - self.page_2.setGeometry(QtCore.QRect(0, 0, 299, 462)) - self.page_2.setObjectName("page_2") - self.gridLayout_8 = QtWidgets.QGridLayout(self.page_2) - self.gridLayout_8.setObjectName("gridLayout_8") - self.groupBox = QtWidgets.QGroupBox(self.page_2) - self.groupBox.setTitle("") - self.groupBox.setObjectName("groupBox") - self.gridLayout_28 = QtWidgets.QGridLayout(self.groupBox) - self.gridLayout_28.setObjectName("gridLayout_28") - self.frame = QtWidgets.QFrame(self.groupBox) - self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel) - self.frame.setFrameShadow(QtWidgets.QFrame.Raised) - self.frame.setObjectName("frame") - self.gridLayout_27 = QtWidgets.QGridLayout(self.frame) - self.gridLayout_27.setObjectName("gridLayout_27") - self.groupBox_9 = QtWidgets.QGroupBox(self.frame) - self.groupBox_9.setObjectName("groupBox_9") - self.gridLayout_24 = QtWidgets.QGridLayout(self.groupBox_9) - self.gridLayout_24.setObjectName("gridLayout_24") - self.verticalLayout_3 = QtWidgets.QVBoxLayout() - self.verticalLayout_3.setObjectName("verticalLayout_3") - self.horizontalLayout_7 = QtWidgets.QHBoxLayout() - self.horizontalLayout_7.setObjectName("horizontalLayout_7") - self.cb_log = QtWidgets.QCheckBox(self.groupBox_9) - self.cb_log.setStyleSheet("") - self.cb_log.setChecked(False) - self.cb_log.setObjectName("cb_log") - self.horizontalLayout_7.addWidget(self.cb_log) - self.cb_norm = QtWidgets.QCheckBox(self.groupBox_9) - self.cb_norm.setObjectName("cb_norm") - self.horizontalLayout_7.addWidget(self.cb_norm) - self.verticalLayout_3.addLayout(self.horizontalLayout_7) - self.cb_transpose = QtWidgets.QCheckBox(self.groupBox_9) - self.cb_transpose.setLayoutDirection(QtCore.Qt.LeftToRight) - self.cb_transpose.setObjectName("cb_transpose") - self.verticalLayout_3.addWidget(self.cb_transpose) - self.gridLayout_24.addLayout(self.verticalLayout_3, 0, 0, 1, 1) - self.gridLayout_27.addWidget(self.groupBox_9, 0, 0, 1, 1) - self.gridLayout_28.addWidget(self.frame, 0, 0, 1, 1) - self.frame_4 = QtWidgets.QFrame(self.groupBox) - self.frame_4.setFrameShape(QtWidgets.QFrame.StyledPanel) - self.frame_4.setFrameShadow(QtWidgets.QFrame.Raised) - self.frame_4.setObjectName("frame_4") - self.gridLayout_25 = QtWidgets.QGridLayout(self.frame_4) - self.gridLayout_25.setObjectName("gridLayout_25") - self.gridLayout_21 = QtWidgets.QGridLayout() - self.gridLayout_21.setObjectName("gridLayout_21") - self.cb_remove_outliers = QtWidgets.QCheckBox(self.frame_4) - self.cb_remove_outliers.setObjectName("cb_remove_outliers") - self.gridLayout_21.addWidget(self.cb_remove_outliers, 0, 0, 1, 1) - self.horizontalLayout = QtWidgets.QHBoxLayout() - self.horizontalLayout.setObjectName("horizontalLayout") - self.hs_nsigma = QtWidgets.QSlider(self.frame_4) - self.hs_nsigma.setEnabled(False) - self.hs_nsigma.setStyleSheet("border-color: rgb(0, 85, 255);") - self.hs_nsigma.setMaximum(200) - self.hs_nsigma.setProperty("value", 3) - self.hs_nsigma.setOrientation(QtCore.Qt.Horizontal) - self.hs_nsigma.setObjectName("hs_nsigma") - self.horizontalLayout.addWidget(self.hs_nsigma) - self.label_nsigma = QtWidgets.QLabel(self.frame_4) - self.label_nsigma.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.label_nsigma.setObjectName("label_nsigma") - self.horizontalLayout.addWidget(self.label_nsigma) - self.gridLayout_21.addLayout(self.horizontalLayout, 1, 0, 1, 1) - self.gridLayout_25.addLayout(self.gridLayout_21, 0, 0, 1, 1) - self.gridLayout_28.addWidget(self.frame_4, 1, 0, 1, 1) - self.frame_3 = QtWidgets.QFrame(self.groupBox) - self.frame_3.setFrameShape(QtWidgets.QFrame.StyledPanel) - self.frame_3.setFrameShadow(QtWidgets.QFrame.Raised) - self.frame_3.setObjectName("frame_3") - self.gridLayout_26 = QtWidgets.QGridLayout(self.frame_3) - self.gridLayout_26.setObjectName("gridLayout_26") - self.gridLayout_5 = QtWidgets.QGridLayout() - self.gridLayout_5.setObjectName("gridLayout_5") - self.horizontalLayout_3 = QtWidgets.QHBoxLayout() - self.horizontalLayout_3.setObjectName("horizontalLayout_3") - self.cb_remove_bg = QtWidgets.QCheckBox(self.frame_3) - self.cb_remove_bg.setObjectName("cb_remove_bg") - self.horizontalLayout_3.addWidget(self.cb_remove_bg) - self.gridLayout_5.addLayout(self.horizontalLayout_3, 0, 0, 1, 1) - self.horizontalLayout_2 = QtWidgets.QHBoxLayout() - self.horizontalLayout_2.setObjectName("horizontalLayout_2") - self.hs_bg_threshold = QtWidgets.QSlider(self.frame_3) - self.hs_bg_threshold.setEnabled(False) - self.hs_bg_threshold.setMaximum(100) - self.hs_bg_threshold.setSingleStep(5) - self.hs_bg_threshold.setPageStep(5) - self.hs_bg_threshold.setProperty("value", 5) - self.hs_bg_threshold.setOrientation(QtCore.Qt.Horizontal) - self.hs_bg_threshold.setObjectName("hs_bg_threshold") - self.horizontalLayout_2.addWidget(self.hs_bg_threshold) - self.label_bg_threshold = QtWidgets.QLabel(self.frame_3) - self.label_bg_threshold.setObjectName("label_bg_threshold") - self.horizontalLayout_2.addWidget(self.label_bg_threshold) - self.gridLayout_5.addLayout(self.horizontalLayout_2, 1, 0, 1, 1) - self.gridLayout_26.addLayout(self.gridLayout_5, 0, 0, 1, 1) - self.gridLayout_28.addWidget(self.frame_3, 2, 0, 1, 1) - self.frame_2 = QtWidgets.QFrame(self.groupBox) - self.frame_2.setFrameShape(QtWidgets.QFrame.StyledPanel) - self.frame_2.setFrameShadow(QtWidgets.QFrame.Raised) - self.frame_2.setObjectName("frame_2") - self.gridLayout_3 = QtWidgets.QGridLayout(self.frame_2) - self.gridLayout_3.setObjectName("gridLayout_3") - self.cb_smooth = QtWidgets.QCheckBox(self.frame_2) - self.cb_smooth.setObjectName("cb_smooth") - self.gridLayout_3.addWidget(self.cb_smooth, 0, 0, 1, 1) - self.horizontalLayout_4 = QtWidgets.QHBoxLayout() - self.horizontalLayout_4.setObjectName("horizontalLayout_4") - self.hs_smooth_size = QtWidgets.QSlider(self.frame_2) - self.hs_smooth_size.setEnabled(False) - self.hs_smooth_size.setMinimum(3) - self.hs_smooth_size.setMaximum(12) - self.hs_smooth_size.setSingleStep(2) - self.hs_smooth_size.setPageStep(2) - self.hs_smooth_size.setProperty("value", 3) - self.hs_smooth_size.setOrientation(QtCore.Qt.Horizontal) - self.hs_smooth_size.setTickPosition(QtWidgets.QSlider.TicksBelow) - self.hs_smooth_size.setTickInterval(2) - self.hs_smooth_size.setObjectName("hs_smooth_size") - self.horizontalLayout_4.addWidget(self.hs_smooth_size) - self.smooth_winow_size = QtWidgets.QLabel(self.frame_2) - self.smooth_winow_size.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.smooth_winow_size.setObjectName("smooth_winow_size") - self.horizontalLayout_4.addWidget(self.smooth_winow_size) - self.gridLayout_3.addLayout(self.horizontalLayout_4, 1, 0, 1, 1) - self.gridLayout_28.addWidget(self.frame_2, 3, 0, 1, 1) - self.gridLayout_8.addWidget(self.groupBox, 0, 0, 1, 1) - self.toolBox.addItem(self.page_2, "") - self.gridLayout_9.addWidget(self.toolBox, 0, 0, 1, 1) - spacerItem2 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) - self.gridLayout_9.addItem(spacerItem2, 2, 0, 1, 1) - self.toolBox_2 = QtWidgets.QToolBox(self.scrollAreaWidgetContents) - self.toolBox_2.setObjectName("toolBox_2") - self.page_5 = QtWidgets.QWidget() - self.page_5.setGeometry(QtCore.QRect(0, 0, 298, 189)) - self.page_5.setObjectName("page_5") - self.gridLayout_15 = QtWidgets.QGridLayout(self.page_5) - self.gridLayout_15.setObjectName("gridLayout_15") - self.gridLayout = QtWidgets.QGridLayout() - self.gridLayout.setObjectName("gridLayout") - self.pb_calc_components = QtWidgets.QPushButton(self.page_5) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.pb_calc_components.sizePolicy().hasHeightForWidth()) - self.pb_calc_components.setSizePolicy(sizePolicy) - self.pb_calc_components.setStyleSheet("background-color: rgb(175, 236, 255);\n" -"color: rgb(255, 0, 127);\n" -"") - self.pb_calc_components.setObjectName("pb_calc_components") - self.gridLayout.addWidget(self.pb_calc_components, 0, 0, 1, 1) - self.pb_pca_scree = QtWidgets.QPushButton(self.page_5) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.pb_pca_scree.sizePolicy().hasHeightForWidth()) - self.pb_pca_scree.setSizePolicy(sizePolicy) - self.pb_pca_scree.setStyleSheet("background-color: rgb(175, 236, 255);\n" -"color: rgb(255, 0, 127);\n" -"") - self.pb_pca_scree.setObjectName("pb_pca_scree") - self.gridLayout.addWidget(self.pb_pca_scree, 1, 0, 1, 1) - self.horizontalLayout_5 = QtWidgets.QHBoxLayout() - self.horizontalLayout_5.setObjectName("horizontalLayout_5") - self.label_11 = QtWidgets.QLabel(self.page_5) - self.label_11.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.label_11.setObjectName("label_11") - self.horizontalLayout_5.addWidget(self.label_11) - self.cb_comp_method = QtWidgets.QComboBox(self.page_5) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.cb_comp_method.sizePolicy().hasHeightForWidth()) - self.cb_comp_method.setSizePolicy(sizePolicy) - self.cb_comp_method.setObjectName("cb_comp_method") - self.cb_comp_method.addItem("") - self.cb_comp_method.addItem("") - self.cb_comp_method.addItem("") - self.cb_comp_method.addItem("") - self.cb_comp_method.addItem("") - self.cb_comp_method.addItem("") - self.cb_comp_method.addItem("") - self.horizontalLayout_5.addWidget(self.cb_comp_method) - self.gridLayout.addLayout(self.horizontalLayout_5, 2, 0, 1, 1) - self.horizontalLayout_6 = QtWidgets.QHBoxLayout() - self.horizontalLayout_6.setObjectName("horizontalLayout_6") - self.label = QtWidgets.QLabel(self.page_5) - self.label.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.label.setObjectName("label") - self.horizontalLayout_6.addWidget(self.label) - self.sb_ncomp = QtWidgets.QSpinBox(self.page_5) - self.sb_ncomp.setMinimum(1) - self.sb_ncomp.setProperty("value", 4) - self.sb_ncomp.setObjectName("sb_ncomp") - self.horizontalLayout_6.addWidget(self.sb_ncomp) - self.gridLayout.addLayout(self.horizontalLayout_6, 3, 0, 1, 1) - self.gridLayout_15.addLayout(self.gridLayout, 0, 0, 1, 1) - self.toolBox_2.addItem(self.page_5, "") - self.page_6 = QtWidgets.QWidget() - self.page_6.setGeometry(QtCore.QRect(0, 0, 308, 185)) - self.page_6.setObjectName("page_6") - self.gridLayout_18 = QtWidgets.QGridLayout(self.page_6) - self.gridLayout_18.setObjectName("gridLayout_18") - self.gridLayout_4 = QtWidgets.QGridLayout() - self.gridLayout_4.setObjectName("gridLayout_4") - self.pb_calc_cluster = QtWidgets.QPushButton(self.page_6) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.pb_calc_cluster.sizePolicy().hasHeightForWidth()) - self.pb_calc_cluster.setSizePolicy(sizePolicy) - self.pb_calc_cluster.setStyleSheet("background-color: rgb(175, 236, 255);\n" -"color: rgb(255, 0, 127);\n" -"") - self.pb_calc_cluster.setObjectName("pb_calc_cluster") - self.gridLayout_4.addWidget(self.pb_calc_cluster, 0, 0, 1, 3) - self.pb_kmeans_elbow = QtWidgets.QPushButton(self.page_6) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.pb_kmeans_elbow.sizePolicy().hasHeightForWidth()) - self.pb_kmeans_elbow.setSizePolicy(sizePolicy) - self.pb_kmeans_elbow.setStyleSheet("background-color: rgb(175, 236, 255);\n" -"color: rgb(255, 0, 127);\n" -"") - self.pb_kmeans_elbow.setObjectName("pb_kmeans_elbow") - self.gridLayout_4.addWidget(self.pb_kmeans_elbow, 1, 0, 1, 3) - self.label_10 = QtWidgets.QLabel(self.page_6) - self.label_10.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.label_10.setObjectName("label_10") - self.gridLayout_4.addWidget(self.label_10, 2, 0, 1, 1) - self.cb_clust_method = QtWidgets.QComboBox(self.page_6) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.cb_clust_method.sizePolicy().hasHeightForWidth()) - self.cb_clust_method.setSizePolicy(sizePolicy) - self.cb_clust_method.setObjectName("cb_clust_method") - self.cb_clust_method.addItem("") - self.cb_clust_method.addItem("") - self.cb_clust_method.addItem("") - self.cb_clust_method.addItem("") - self.cb_clust_method.addItem("") - self.cb_clust_method.addItem("") - self.gridLayout_4.addWidget(self.cb_clust_method, 2, 1, 1, 2) - self.label_2 = QtWidgets.QLabel(self.page_6) - self.label_2.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.label_2.setObjectName("label_2") - self.gridLayout_4.addWidget(self.label_2, 3, 0, 1, 2) - self.sb_ncluster = QtWidgets.QSpinBox(self.page_6) - self.sb_ncluster.setMinimum(1) - self.sb_ncluster.setProperty("value", 4) - self.sb_ncluster.setObjectName("sb_ncluster") - self.gridLayout_4.addWidget(self.sb_ncluster, 3, 2, 1, 1) - self.gridLayout_18.addLayout(self.gridLayout_4, 0, 0, 1, 1) - self.toolBox_2.addItem(self.page_6, "") - self.page_7 = QtWidgets.QWidget() - self.page_7.setGeometry(QtCore.QRect(0, 0, 299, 156)) - self.page_7.setObjectName("page_7") - self.gridLayout_20 = QtWidgets.QGridLayout(self.page_7) - self.gridLayout_20.setObjectName("gridLayout_20") - self.gridLayout_2 = QtWidgets.QGridLayout() - self.gridLayout_2.setObjectName("gridLayout_2") - self.pb_elist_xanes = QtWidgets.QPushButton(self.page_7) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.pb_elist_xanes.sizePolicy().hasHeightForWidth()) - self.pb_elist_xanes.setSizePolicy(sizePolicy) - self.pb_elist_xanes.setStyleSheet("background-color: rgb(175, 236, 255);\n" -"color: rgb(255, 0, 127);\n" -"") - self.pb_elist_xanes.setObjectName("pb_elist_xanes") - self.gridLayout_2.addWidget(self.pb_elist_xanes, 0, 0, 1, 2) - self.cb_kev_flag = QtWidgets.QCheckBox(self.page_7) - self.cb_kev_flag.setObjectName("cb_kev_flag") - self.gridLayout_2.addWidget(self.cb_kev_flag, 0, 2, 1, 1) - self.pb_ref_xanes = QtWidgets.QPushButton(self.page_7) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.pb_ref_xanes.sizePolicy().hasHeightForWidth()) - self.pb_ref_xanes.setSizePolicy(sizePolicy) - self.pb_ref_xanes.setStyleSheet("background-color: rgb(175, 236, 255);\n" -"color: rgb(255, 0, 127);\n" -"") - self.pb_ref_xanes.setObjectName("pb_ref_xanes") - self.gridLayout_2.addWidget(self.pb_ref_xanes, 1, 0, 1, 2) - self.pb_plot_refs = QtWidgets.QPushButton(self.page_7) - self.pb_plot_refs.setStyleSheet("background-color: rgb(175, 236, 255);\n" -"color: rgb(255, 0, 127);") - self.pb_plot_refs.setObjectName("pb_plot_refs") - self.gridLayout_2.addWidget(self.pb_plot_refs, 1, 2, 1, 1) - self.cb_xanes_fitting_method = QtWidgets.QComboBox(self.page_7) - self.cb_xanes_fitting_method.setObjectName("cb_xanes_fitting_method") - self.cb_xanes_fitting_method.addItem("") - self.gridLayout_2.addWidget(self.cb_xanes_fitting_method, 2, 0, 1, 1) - self.pb_xanes_fit = QtWidgets.QPushButton(self.page_7) - self.pb_xanes_fit.setEnabled(False) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Minimum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.pb_xanes_fit.sizePolicy().hasHeightForWidth()) - self.pb_xanes_fit.setSizePolicy(sizePolicy) - self.pb_xanes_fit.setStyleSheet("background-color: rgb(175, 236, 255);\n" -"color: rgb(255, 0, 127);\n" -"") - self.pb_xanes_fit.setObjectName("pb_xanes_fit") - self.gridLayout_2.addWidget(self.pb_xanes_fit, 2, 1, 1, 2) - self.gridLayout_20.addLayout(self.gridLayout_2, 0, 0, 1, 1) - spacerItem3 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) - self.gridLayout_20.addItem(spacerItem3, 1, 0, 1, 1) - self.toolBox_2.addItem(self.page_7, "") - self.gridLayout_9.addWidget(self.toolBox_2, 3, 0, 1, 1) - self.scrollArea.setWidget(self.scrollAreaWidgetContents) - self.gridLayout_16.addWidget(self.scrollArea, 0, 0, 1, 1) - self.groupBox_6 = QtWidgets.QGroupBox(self.centralwidget) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.groupBox_6.sizePolicy().hasHeightForWidth()) - self.groupBox_6.setSizePolicy(sizePolicy) - self.groupBox_6.setStyleSheet("font:12pt \"Segoe UI\";") - self.groupBox_6.setTitle("") - self.groupBox_6.setObjectName("groupBox_6") - self.gridLayout_29 = QtWidgets.QGridLayout(self.groupBox_6) - self.gridLayout_29.setObjectName("gridLayout_29") - self.frame_5 = QtWidgets.QFrame(self.groupBox_6) - self.frame_5.setFrameShape(QtWidgets.QFrame.StyledPanel) - self.frame_5.setFrameShadow(QtWidgets.QFrame.Raised) - self.frame_5.setObjectName("frame_5") - self.gridLayout_23 = QtWidgets.QGridLayout(self.frame_5) - self.gridLayout_23.setObjectName("gridLayout_23") - self.groupBox_8 = QtWidgets.QGroupBox(self.frame_5) - self.groupBox_8.setStyleSheet("font: 9pt \"Segoe UI\";") - self.groupBox_8.setObjectName("groupBox_8") - self.gridLayout_19 = QtWidgets.QGridLayout(self.groupBox_8) - self.gridLayout_19.setObjectName("gridLayout_19") - self.groupBox_5 = QtWidgets.QGroupBox(self.groupBox_8) - self.groupBox_5.setObjectName("groupBox_5") - self.gridLayout_11 = QtWidgets.QGridLayout(self.groupBox_5) - self.gridLayout_11.setObjectName("gridLayout_11") - self.le_spec_roi_size = QtWidgets.QLabel(self.groupBox_5) - self.le_spec_roi_size.setStyleSheet("color: rgb(255, 0,0);") - self.le_spec_roi_size.setObjectName("le_spec_roi_size") - self.gridLayout_11.addWidget(self.le_spec_roi_size, 1, 1, 1, 1) - self.label_8 = QtWidgets.QLabel(self.groupBox_5) - self.label_8.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.label_8.setObjectName("label_8") - self.gridLayout_11.addWidget(self.label_8, 0, 0, 1, 1) - self.label_29 = QtWidgets.QLabel(self.groupBox_5) - self.label_29.setStyleSheet("") - self.label_29.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.label_29.setObjectName("label_29") - self.gridLayout_11.addWidget(self.label_29, 1, 0, 1, 1) - self.le_spec_roi = QtWidgets.QLabel(self.groupBox_5) - self.le_spec_roi.setStyleSheet("color: rgb(255, 0,0);") - self.le_spec_roi.setObjectName("le_spec_roi") - self.gridLayout_11.addWidget(self.le_spec_roi, 0, 1, 1, 1) - self.gridLayout_19.addWidget(self.groupBox_5, 0, 1, 1, 1) - self.groupBox_4 = QtWidgets.QGroupBox(self.groupBox_8) - self.groupBox_4.setObjectName("groupBox_4") - self.gridLayout_12 = QtWidgets.QGridLayout(self.groupBox_4) - self.gridLayout_12.setObjectName("gridLayout_12") - self.label_4 = QtWidgets.QLabel(self.groupBox_4) - self.label_4.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.label_4.setObjectName("label_4") - self.gridLayout_12.addWidget(self.label_4, 0, 0, 1, 1) - self.le_roi = QtWidgets.QLabel(self.groupBox_4) - self.le_roi.setStyleSheet("color: rgb(255, 0,0);") - self.le_roi.setObjectName("le_roi") - self.gridLayout_12.addWidget(self.le_roi, 0, 1, 1, 1) - self.label_28 = QtWidgets.QLabel(self.groupBox_4) - self.label_28.setStyleSheet("") - self.label_28.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.label_28.setObjectName("label_28") - self.gridLayout_12.addWidget(self.label_28, 1, 0, 1, 1) - self.le_roi_size = QtWidgets.QLabel(self.groupBox_4) - self.le_roi_size.setStyleSheet("color: rgb(255, 0, 0);") - self.le_roi_size.setObjectName("le_roi_size") - self.gridLayout_12.addWidget(self.le_roi_size, 1, 1, 1, 1) - self.gridLayout_19.addWidget(self.groupBox_4, 0, 0, 1, 1) - self.gridLayout_23.addWidget(self.groupBox_8, 0, 0, 1, 1) - self.groupBox_3 = QtWidgets.QGroupBox(self.frame_5) - self.groupBox_3.setObjectName("groupBox_3") - self.gridLayout_6 = QtWidgets.QGridLayout(self.groupBox_3) - self.gridLayout_6.setObjectName("gridLayout_6") - self.rb_math_roi_img = QtWidgets.QRadioButton(self.groupBox_3) - self.rb_math_roi_img.setLayoutDirection(QtCore.Qt.LeftToRight) - self.rb_math_roi_img.setObjectName("rb_math_roi_img") - self.gridLayout_6.addWidget(self.rb_math_roi_img, 0, 0, 1, 1) - self.pb_reset_roi_2 = QtWidgets.QPushButton(self.groupBox_3) - self.pb_reset_roi_2.setStyleSheet("background-color: rgb(175, 236, 255);") - self.pb_reset_roi_2.setObjectName("pb_reset_roi_2") - self.gridLayout_6.addWidget(self.pb_reset_roi_2, 0, 1, 1, 1) - self.label_14 = QtWidgets.QLabel(self.groupBox_3) - self.label_14.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.label_14.setObjectName("label_14") - self.gridLayout_6.addWidget(self.label_14, 1, 0, 1, 1) - self.cb_img_roi_action = QtWidgets.QComboBox(self.groupBox_3) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.cb_img_roi_action.sizePolicy().hasHeightForWidth()) - self.cb_img_roi_action.setSizePolicy(sizePolicy) - self.cb_img_roi_action.setObjectName("cb_img_roi_action") - self.cb_img_roi_action.addItem("") - self.cb_img_roi_action.addItem("") - self.cb_img_roi_action.addItem("") - self.cb_img_roi_action.addItem("") - self.gridLayout_6.addWidget(self.cb_img_roi_action, 1, 1, 1, 1) - self.gridLayout_23.addWidget(self.groupBox_3, 3, 0, 1, 1) - self.groupBox_7 = QtWidgets.QGroupBox(self.frame_5) - self.groupBox_7.setObjectName("groupBox_7") - self.gridLayout_14 = QtWidgets.QGridLayout(self.groupBox_7) - self.gridLayout_14.setObjectName("gridLayout_14") - self.rb_math_roi = QtWidgets.QRadioButton(self.groupBox_7) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.rb_math_roi.sizePolicy().hasHeightForWidth()) - self.rb_math_roi.setSizePolicy(sizePolicy) - self.rb_math_roi.setCheckable(True) - self.rb_math_roi.setChecked(False) - self.rb_math_roi.setObjectName("rb_math_roi") - self.gridLayout_14.addWidget(self.rb_math_roi, 0, 0, 1, 2) - self.label_13 = QtWidgets.QLabel(self.groupBox_7) - self.label_13.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.label_13.setObjectName("label_13") - self.gridLayout_14.addWidget(self.label_13, 1, 0, 1, 1) - self.cb_roi_operation = QtWidgets.QComboBox(self.groupBox_7) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.cb_roi_operation.sizePolicy().hasHeightForWidth()) - self.cb_roi_operation.setSizePolicy(sizePolicy) - self.cb_roi_operation.setObjectName("cb_roi_operation") - self.cb_roi_operation.addItem("") - self.cb_roi_operation.addItem("") - self.cb_roi_operation.addItem("") - self.cb_roi_operation.addItem("") - self.gridLayout_14.addWidget(self.cb_roi_operation, 1, 1, 1, 1) - self.gridLayout_23.addWidget(self.groupBox_7, 10, 0, 1, 1) - self.pb_save_disp_img = QtWidgets.QPushButton(self.frame_5) - self.pb_save_disp_img.setStyleSheet("background-color: rgb(175, 236, 255);\n" -"color: rgb(255, 0, 127);") - self.pb_save_disp_img.setObjectName("pb_save_disp_img") - self.gridLayout_23.addWidget(self.pb_save_disp_img, 13, 0, 1, 1) - spacerItem4 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) - self.gridLayout_23.addItem(spacerItem4, 4, 0, 1, 1) - self.pb_save_disp_spec = QtWidgets.QPushButton(self.frame_5) - self.pb_save_disp_spec.setStyleSheet("background-color: rgb(175, 236, 255);\n" -"color: rgb(255, 0, 127);") - self.pb_save_disp_spec.setObjectName("pb_save_disp_spec") - self.gridLayout_23.addWidget(self.pb_save_disp_spec, 14, 0, 1, 1) - spacerItem5 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) - self.gridLayout_23.addItem(spacerItem5, 12, 0, 1, 1) - self.groupBox_2 = QtWidgets.QGroupBox(self.frame_5) - self.groupBox_2.setObjectName("groupBox_2") - self.gridLayout_22 = QtWidgets.QGridLayout(self.groupBox_2) - self.gridLayout_22.setObjectName("gridLayout_22") - self.verticalLayout = QtWidgets.QVBoxLayout() - self.verticalLayout.setObjectName("verticalLayout") - self.rb_poly_roi = QtWidgets.QRadioButton(self.groupBox_2) - self.rb_poly_roi.setChecked(True) - self.rb_poly_roi.setObjectName("rb_poly_roi") - self.verticalLayout.addWidget(self.rb_poly_roi) - self.rb_rect_roi = QtWidgets.QRadioButton(self.groupBox_2) - self.rb_rect_roi.setObjectName("rb_rect_roi") - self.verticalLayout.addWidget(self.rb_rect_roi) - self.gridLayout_22.addLayout(self.verticalLayout, 0, 0, 1, 2) - self.verticalLayout_2 = QtWidgets.QVBoxLayout() - self.verticalLayout_2.setObjectName("verticalLayout_2") - self.rb_elli_roi = QtWidgets.QRadioButton(self.groupBox_2) - self.rb_elli_roi.setObjectName("rb_elli_roi") - self.verticalLayout_2.addWidget(self.rb_elli_roi) - self.rb_circle_roi = QtWidgets.QRadioButton(self.groupBox_2) - self.rb_circle_roi.setObjectName("rb_circle_roi") - self.verticalLayout_2.addWidget(self.rb_circle_roi) - self.gridLayout_22.addLayout(self.verticalLayout_2, 0, 2, 1, 1) - self.rb_line_roi = QtWidgets.QRadioButton(self.groupBox_2) - self.rb_line_roi.setObjectName("rb_line_roi") - self.gridLayout_22.addWidget(self.rb_line_roi, 1, 0, 1, 1) - self.gridLayout_23.addWidget(self.groupBox_2, 1, 0, 1, 1) - spacerItem6 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) - self.gridLayout_23.addItem(spacerItem6, 2, 0, 1, 1) - self.gridLayout_29.addWidget(self.frame_5, 0, 2, 2, 1) - self.image_view = ImageView(self.groupBox_6) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.image_view.sizePolicy().hasHeightForWidth()) - self.image_view.setSizePolicy(sizePolicy) - self.image_view.setObjectName("image_view") - self.gridLayout_29.addWidget(self.image_view, 0, 1, 1, 1) - self.spectrum_view = PlotWidget(self.groupBox_6) - self.spectrum_view.setObjectName("spectrum_view") - self.gridLayout_29.addWidget(self.spectrum_view, 1, 1, 1, 1) - self.gridLayout_16.addWidget(self.groupBox_6, 0, 1, 1, 1) - MainWindow.setCentralWidget(self.centralwidget) - self.menubar = QtWidgets.QMenuBar(MainWindow) - self.menubar.setGeometry(QtCore.QRect(0, 0, 1246, 29)) - self.menubar.setObjectName("menubar") - self.menuManual = QtWidgets.QMenu(self.menubar) - self.menuManual.setObjectName("menuManual") - self.menuFile = QtWidgets.QMenu(self.menubar) - self.menuFile.setObjectName("menuFile") - self.menuMask = QtWidgets.QMenu(self.menubar) - self.menuMask.setObjectName("menuMask") - MainWindow.setMenuBar(self.menubar) - self.statusbar_main = QtWidgets.QStatusBar(MainWindow) - self.statusbar_main.setObjectName("statusbar_main") - MainWindow.setStatusBar(self.statusbar_main) - self.actionOpen_PDF = QtWidgets.QAction(MainWindow) - self.actionOpen_PDF.setObjectName("actionOpen_PDF") - self.actionOpen_in_GitHub = QtWidgets.QAction(MainWindow) - self.actionOpen_in_GitHub.setObjectName("actionOpen_in_GitHub") - self.actionOpen_Image_Data = QtWidgets.QAction(MainWindow) - self.actionOpen_Image_Data.setEnabled(True) - self.actionOpen_Image_Data.setObjectName("actionOpen_Image_Data") - self.actionClose = QtWidgets.QAction(MainWindow) - self.actionClose.setObjectName("actionClose") - self.actionExit = QtWidgets.QAction(MainWindow) - self.actionExit.setObjectName("actionExit") - self.actionSave_as = QtWidgets.QAction(MainWindow) - self.actionSave_as.setObjectName("actionSave_as") - self.actionOpen_PyXRF = QtWidgets.QAction(MainWindow) - self.actionOpen_PyXRF.setObjectName("actionOpen_PyXRF") - self.actionOpen_Image_J = QtWidgets.QAction(MainWindow) - self.actionOpen_Image_J.setObjectName("actionOpen_Image_J") - self.actionOpen_TomViz = QtWidgets.QAction(MainWindow) - self.actionOpen_TomViz.setObjectName("actionOpen_TomViz") - self.actionOpen_Mantis = QtWidgets.QAction(MainWindow) - self.actionOpen_Mantis.setObjectName("actionOpen_Mantis") - self.actionOpen_Athena = QtWidgets.QAction(MainWindow) - self.actionOpen_Athena.setObjectName("actionOpen_Athena") - self.actionDataBroker = QtWidgets.QAction(MainWindow) - self.actionDataBroker.setObjectName("actionDataBroker") - self.actionOpen_HXN_DB = QtWidgets.QAction(MainWindow) - self.actionOpen_HXN_DB.setObjectName("actionOpen_HXN_DB") - self.actionLoad_Energy = QtWidgets.QAction(MainWindow) - self.actionLoad_Energy.setObjectName("actionLoad_Energy") - self.actionOpen_Multiple_Files = QtWidgets.QAction(MainWindow) - self.actionOpen_Multiple_Files.setObjectName("actionOpen_Multiple_Files") - self.actionOpen_Mask_Gen = QtWidgets.QAction(MainWindow) - self.actionOpen_Mask_Gen.setObjectName("actionOpen_Mask_Gen") - self.actionCreate_elist_from_log = QtWidgets.QAction(MainWindow) - self.actionCreate_elist_from_log.setObjectName("actionCreate_elist_from_log") - self.actionSave_Energy_List = QtWidgets.QAction(MainWindow) - self.actionSave_Energy_List.setObjectName("actionSave_Energy_List") - self.actionAlign_Stack = QtWidgets.QAction(MainWindow) - self.actionAlign_Stack.setObjectName("actionAlign_Stack") - self.menuManual.addSeparator() - self.menuManual.addAction(self.actionOpen_in_GitHub) - self.menuFile.addAction(self.actionOpen_Image_Data) - self.menuFile.addAction(self.actionOpen_Multiple_Files) - self.menuFile.addAction(self.actionLoad_Energy) - self.menuFile.addAction(self.actionSave_Energy_List) - self.menuFile.addAction(self.actionSave_as) - self.menuFile.addAction(self.actionExit) - self.menuMask.addAction(self.actionOpen_Mask_Gen) - self.menuMask.addAction(self.actionAlign_Stack) - self.menubar.addAction(self.menuFile.menuAction()) - self.menubar.addAction(self.menuMask.menuAction()) - self.menubar.addAction(self.menuManual.menuAction()) - - self.retranslateUi(MainWindow) - self.toolBox.setCurrentIndex(1) - self.toolBox_2.setCurrentIndex(2) - QtCore.QMetaObject.connectSlotsByName(MainWindow) - - def retranslateUi(self, MainWindow): - _translate = QtCore.QCoreApplication.translate - MainWindow.setWindowTitle(_translate("MainWindow", "NSLS-II MIDAS")) - self.pb_reset_img.setText(_translate("MainWindow", "Reset Image")) - self.label_5.setText(_translate("MainWindow", "to")) - self.label_9.setText(_translate("MainWindow", "to")) - self.sb_yrange2.setSuffix(_translate("MainWindow", " px")) - self.sb_xrange1.setSuffix(_translate("MainWindow", " px")) - self.sb_xrange2.setSuffix(_translate("MainWindow", " px")) - self.label_22.setText(_translate("MainWindow", "to")) - self.label_19.setText(_translate("MainWindow", "X Dimension")) - self.sb_yrange1.setSuffix(_translate("MainWindow", " px")) - self.label_15.setText(_translate("MainWindow", "Y Dimension")) - self.label_20.setText(_translate("MainWindow", "Stack Range ")) - self.pb_crop.setToolTip(_translate("MainWindow", "Adjust the dimensions of the image")) - self.pb_crop.setText(_translate("MainWindow", "Update")) - self.cb_rebin.setText(_translate("MainWindow", "rebin")) - self.cb_upscale.setText(_translate("MainWindow", "Upscale")) - self.cb_remove_edges.setToolTip(_translate("MainWindow", "Removes one column/row from all four edges")) - self.cb_remove_edges.setText(_translate("MainWindow", "Remove Edges")) - self.toolBox.setItemText(self.toolBox.indexOf(self.page), _translate("MainWindow", "Adjust Image Dimensions")) - self.groupBox_9.setTitle(_translate("MainWindow", "Transform")) - self.cb_log.setToolTip(_translate("MainWindow", "Covert image dat to log values.")) - self.cb_log.setText(_translate("MainWindow", "Log ")) - self.cb_norm.setToolTip(_translate("MainWindow", "Intensity Normalization with the last Frame.")) - self.cb_norm.setText(_translate("MainWindow", "Normalize")) - self.cb_transpose.setToolTip(_translate("MainWindow", "Reverse the arrays. 012 will be 210")) - self.cb_transpose.setText(_translate("MainWindow", "Transpose")) - self.cb_remove_outliers.setText(_translate("MainWindow", "Remove Outliers (NSigma)")) - self.label_nsigma.setText(_translate("MainWindow", "1")) - self.cb_remove_bg.setText(_translate("MainWindow", "Thresholding")) - self.label_bg_threshold.setText(_translate("MainWindow", "5")) - self.cb_smooth.setToolTip(_translate("MainWindow", "uses savgol_filter to smooth data")) - self.cb_smooth.setText(_translate("MainWindow", "Smoothen")) - self.smooth_winow_size.setText(_translate("MainWindow", "Window size")) - self.toolBox.setItemText(self.toolBox.indexOf(self.page_2), _translate("MainWindow", "Image Processing")) - self.pb_calc_components.setText(_translate("MainWindow", "Calculate Components")) - self.pb_pca_scree.setText(_translate("MainWindow", "PCA Scree Plot")) - self.label_11.setText(_translate("MainWindow", "Method")) - self.cb_comp_method.setItemText(0, _translate("MainWindow", "PCA")) - self.cb_comp_method.setItemText(1, _translate("MainWindow", "NMF")) - self.cb_comp_method.setItemText(2, _translate("MainWindow", "FastICA")) - self.cb_comp_method.setItemText(3, _translate("MainWindow", "IncrementalPCA")) - self.cb_comp_method.setItemText(4, _translate("MainWindow", "TruncatedSVD")) - self.cb_comp_method.setItemText(5, _translate("MainWindow", "FactorAnalysis")) - self.cb_comp_method.setItemText(6, _translate("MainWindow", "DictionaryLearning")) - self.label.setText(_translate("MainWindow", "Number of Components")) - self.toolBox_2.setItemText(self.toolBox_2.indexOf(self.page_5), _translate("MainWindow", "Component Analysis")) - self.pb_calc_cluster.setText(_translate("MainWindow", "Calculate Clusters")) - self.pb_kmeans_elbow.setText(_translate("MainWindow", "KMeans Variance Plot")) - self.label_10.setText(_translate("MainWindow", "Method")) - self.cb_clust_method.setItemText(0, _translate("MainWindow", "KMeans")) - self.cb_clust_method.setItemText(1, _translate("MainWindow", "MiniBatchKMeans")) - self.cb_clust_method.setItemText(2, _translate("MainWindow", "MeanShift")) - self.cb_clust_method.setItemText(3, _translate("MainWindow", "Spectral Clustering")) - self.cb_clust_method.setItemText(4, _translate("MainWindow", "Correlation-Kmeans")) - self.cb_clust_method.setItemText(5, _translate("MainWindow", "Affinity Propagation")) - self.label_2.setText(_translate("MainWindow", "Number of Clusters")) - self.toolBox_2.setItemText(self.toolBox_2.indexOf(self.page_6), _translate("MainWindow", "Cluster Analysis")) - self.pb_elist_xanes.setText(_translate("MainWindow", "Load Energy List")) - self.cb_kev_flag.setText(_translate("MainWindow", "keV")) - self.pb_ref_xanes.setText(_translate("MainWindow", "Load Ref. Spec.")) - self.pb_plot_refs.setText(_translate("MainWindow", "Plot")) - self.cb_xanes_fitting_method.setItemText(0, _translate("MainWindow", "NNLS")) - self.pb_xanes_fit.setText(_translate("MainWindow", " Fit")) - self.toolBox_2.setItemText(self.toolBox_2.indexOf(self.page_7), _translate("MainWindow", "XANES Fitting")) - self.groupBox_8.setTitle(_translate("MainWindow", "ROI Positions")) - self.groupBox_5.setTitle(_translate("MainWindow", "Spectrum ROI")) - self.le_spec_roi_size.setText(_translate("MainWindow", "roi_size")) - self.label_8.setText(_translate("MainWindow", "Range (eV):")) - self.label_29.setText(_translate("MainWindow", "Size (eV):")) - self.le_spec_roi.setText(_translate("MainWindow", "roix, roiy")) - self.groupBox_4.setTitle(_translate("MainWindow", "Image ROI")) - self.label_4.setText(_translate("MainWindow", "Position:")) - self.le_roi.setText(_translate("MainWindow", "roix, roiy")) - self.label_28.setText(_translate("MainWindow", "Size(pixels):")) - self.le_roi_size.setText(_translate("MainWindow", "roi_size")) - self.groupBox_3.setTitle(_translate("MainWindow", "Image Calculations")) - self.rb_math_roi_img.setText(_translate("MainWindow", "Add Math ROI")) - self.pb_reset_roi_2.setText(_translate("MainWindow", "Reset")) - self.label_14.setText(_translate("MainWindow", "Action:")) - self.cb_img_roi_action.setItemText(0, _translate("MainWindow", "Subtract")) - self.cb_img_roi_action.setItemText(1, _translate("MainWindow", "Divide")) - self.cb_img_roi_action.setItemText(2, _translate("MainWindow", "Add")) - self.cb_img_roi_action.setItemText(3, _translate("MainWindow", "Compare")) - self.groupBox_7.setTitle(_translate("MainWindow", "Spectrum Calculations")) - self.rb_math_roi.setText(_translate("MainWindow", "Add Math ROI")) - self.label_13.setText(_translate("MainWindow", "Action:")) - self.cb_roi_operation.setItemText(0, _translate("MainWindow", "Divide")) - self.cb_roi_operation.setItemText(1, _translate("MainWindow", "Subtract")) - self.cb_roi_operation.setItemText(2, _translate("MainWindow", "Add")) - self.cb_roi_operation.setItemText(3, _translate("MainWindow", "Correlation Plot")) - self.pb_save_disp_img.setText(_translate("MainWindow", "Save Current Image (.tiff)")) - self.pb_save_disp_spec.setText(_translate("MainWindow", "Save Current Spectrum (.txt)")) - self.groupBox_2.setTitle(_translate("MainWindow", "ROI Shape")) - self.rb_poly_roi.setText(_translate("MainWindow", "Polygon (default)")) - self.rb_rect_roi.setText(_translate("MainWindow", "Rectangle")) - self.rb_elli_roi.setText(_translate("MainWindow", "Ellipse")) - self.rb_circle_roi.setText(_translate("MainWindow", "Circle")) - self.rb_line_roi.setText(_translate("MainWindow", "Line")) - self.menuManual.setTitle(_translate("MainWindow", "Help")) - self.menuFile.setTitle(_translate("MainWindow", "File")) - self.menuMask.setTitle(_translate("MainWindow", "Tools")) - self.actionOpen_PDF.setText(_translate("MainWindow", "Open PDF")) - self.actionOpen_in_GitHub.setText(_translate("MainWindow", "Open in GitHub (most updated)")) - self.actionOpen_Image_Data.setText(_translate("MainWindow", "Open Image Data")) - self.actionOpen_Image_Data.setToolTip(_translate("MainWindow", "Support all tiff and specific h5 files")) - self.actionOpen_Image_Data.setShortcut(_translate("MainWindow", "Ctrl+O")) - self.actionClose.setText(_translate("MainWindow", "Close")) - self.actionExit.setText(_translate("MainWindow", "Exit")) - self.actionExit.setShortcut(_translate("MainWindow", "Ctrl+Q")) - self.actionSave_as.setText(_translate("MainWindow", "Export Tiff Stack")) - self.actionSave_as.setToolTip(_translate("MainWindow", "Save the displayed/Modified stack as a tiff file")) - self.actionSave_as.setShortcut(_translate("MainWindow", "Ctrl+S")) - self.actionOpen_PyXRF.setText(_translate("MainWindow", "Open PyXRF")) - self.actionOpen_Image_J.setText(_translate("MainWindow", "Open Image J")) - self.actionOpen_TomViz.setText(_translate("MainWindow", "Open TomViz")) - self.actionOpen_Mantis.setText(_translate("MainWindow", "Open Mantis")) - self.actionOpen_Athena.setText(_translate("MainWindow", "Open Athena")) - self.actionDataBroker.setText(_translate("MainWindow", "DataBroker")) - self.actionOpen_HXN_DB.setText(_translate("MainWindow", "Open HXN DB")) - self.actionLoad_Energy.setText(_translate("MainWindow", "Load Energy")) - self.actionLoad_Energy.setToolTip(_translate("MainWindow", "Load list of energies for XANES stack. Supports only .txt fromat")) - self.actionLoad_Energy.setShortcut(_translate("MainWindow", "Ctrl+E")) - self.actionOpen_Multiple_Files.setText(_translate("MainWindow", "Open Multiple Files")) - self.actionOpen_Multiple_Files.setToolTip(_translate("MainWindow", "Create a stack from multiple tiff images of same shape")) - self.actionOpen_Multiple_Files.setShortcut(_translate("MainWindow", "Ctrl+M")) - self.actionOpen_Mask_Gen.setText(_translate("MainWindow", "Open Mask Generator")) - self.actionOpen_Mask_Gen.setToolTip(_translate("MainWindow", "A new window will be opened to creat threshold based masks")) - self.actionCreate_elist_from_log.setText(_translate("MainWindow", "Create elist from log file")) - self.actionSave_Energy_List.setText(_translate("MainWindow", "Save Energy List")) - self.actionAlign_Stack.setText(_translate("MainWindow", "Align Stack")) - self.actionAlign_Stack.setToolTip(_translate("MainWindow", "A new window will be opened to align images in a stack")) -from pyqtgraph import ImageView, PlotWidget - - -if __name__ == "__main__": - import sys - app = QtWidgets.QApplication(sys.argv) - MainWindow = QtWidgets.QMainWindow() - ui = Ui_MainWindow() - ui.setupUi(MainWindow) - MainWindow.show() - sys.exit(app.exec_()) diff --git a/xmidas/uis/midasMainwindow.ui b/xmidas/uis/midasMainwindow.ui index eb98ef8..dcd166c 100644 --- a/xmidas/uis/midasMainwindow.ui +++ b/xmidas/uis/midasMainwindow.ui @@ -1,2938 +1,3034 @@ - - - Ajith - MainWindow - - - true - - - - 0 - 0 - 1232 - 931 - - - - - 0 - 0 - - - - NSLS-II MIDAS - - - - pancake.icopancake.ico - - - font: 10pt "Segoe UI"; - - - - - true - - - - 0 - 0 - - - - QWidget { -font: 10pt "Segoe UI"; -} - -QPushButton { -background-color: rgb(175, 236, 255); -color: rgb(255, 5,0); -font: 10pt "Segoe UI"; -} - -QLabel { -font: 10pt "Segoe UI"; -} - -QSlider::groove:horizontal { -border: 1px solid #bbb; -background: white; -height: 10px; -border-radius: 4px; -} - -QSlider::sub-page:horizontal { -background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #66e, stop: 1 #bbf); -background: qlineargradient(x1: 0, y1: 0.2, x2: 1, y2: 1, - stop: 0 #bbf, stop: 1 #55f); -border: 1px solid #777; -height: 10px; -border-radius: 4px; -} - -QSlider::add-page:horizontal { -background: #fff; -border: 1px solid #777; -height: 10px; -border-radius: 4px; -} - -QSlider::handle:horizontal { -background: qlineargradient(x1:0, y1:0, x2:1, y2:1, - stop:0 #eee, stop:1 #ccc); -border: 1px solid #777; -width: 13px; -margin-top: -2px; -margin-bottom: -2px; -border-radius: 4px; -} - -QSlider::handle:horizontal:hover { -background: qlineargradient(x1:0, y1:0, x2:1, y2:1, - stop:0 #fff, stop:1 #ddd); -border: 1px solid #444; -border-radius: 2px; -} - -QSlider::sub-page:horizontal:disabled { -background: #bbb; -border-color: #999; -} - -QSlider::add-page:horizontal:disabled { -background: #eee; -border-color: #999; -} - -QSlider::handle:horizontal:disabled { -background: #eee; -border: 1px solid #aaa; -border-radius: 4px; -} - - - - - - - - - - 0 - 0 - - - - QFrame::StyledPanel - - - QFrame::Sunken - - - - - - - 0 - 0 - - - - - - - 2 - - - - - 0 - 0 - 368 - 325 - - - - Adjust Image Dimensions - - - - - - - 0 - 0 - - - - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - QLayout::SetMinimumSize - - - - - - 0 - 0 - - - - 0 - - - 500 - - - 0 - - - 10 - - - - - - - - 0 - 0 - - - - 1 - - - 5000 - - - 1200 - - - 10 - - - - - - - - 0 - 0 - - - - false - - - px - - - 1 - - - 10000 - - - 100 - - - - - - - - 0 - 0 - - - - false - - - px - - - 1 - - - 10000 - - - 100 - - - - - - - - 0 - 0 - - - - false - - - px - - - 0 - - - 10000 - - - 0 - - - - - - - - 0 - 0 - - - - Y Dimension - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 0 - 0 - - - - false - - - px - - - 0 - - - 10000 - - - 0 - - - - - - - - 0 - 0 - - - - X Dimension - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 0 - 0 - - - - Stack Range - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 0 - 0 - - - - to - - - Qt::AlignCenter - - - - - - - - 0 - 0 - - - - to - - - Qt::AlignCenter - - - - - - - - 0 - 0 - - - - to - - - Qt::AlignCenter - - - - - - - - - - 0 - 0 - - - - Adjust the dimensions of the image - - - - - - Update - - - - - - - - - - - 0 - 0 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - - - - 0 - 0 - - - - Removes one column/row from all four edges - - - Remove Edges - - - - - - - - - true - - - - 0 - 0 - - - - rebin - - - - - - - true - - - - 0 - 0 - - - - Upscale - - - - - - - - - Ratio - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - true - - - - 0 - 0 - - - - font: 10pt "MS Shell Dlg 2"; - - - 2 - - - 16 - - - 2 - - - 2 - - - - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - 0 - 0 - 300 - 392 - - - - Image Processing - - - - - - - - - reverses the image axes (ZXY) to (YXZ) - - - Transpose - - - - - - - swaps 2nd and 3rd axes (ZXY) to (ZYX) - - - Swap XY Axes - - - - - - - - - - - - 0 - 0 - - - - Covert image dat to log values. - - - - - - Log - - - false - - - - - - - - 0 - 0 - - - - Intensity Normalization with the last Frame. - - - Normalize to Max - - - - - - - - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - - - Remove Outliers (NSigma) - - - - - - - false - - - - - - 200 - - - 3 - - - Qt::Horizontal - - - - - - - 1 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - - - - - Thresholding - - - - - - - - - - - false - - - 100 - - - 5 - - - 5 - - - 5 - - - Qt::Horizontal - - - - - - - 5 - - - - - - - - - - - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - uses savgol_filter to smooth data - - - Smoothen - - - - - - - - - false - - - 3 - - - 12 - - - 2 - - - 2 - - - 3 - - - Qt::Horizontal - - - QSlider::TicksBelow - - - 2 - - - - - - - Window size - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - - - - - - - 0 - 0 - 379 - 309 - - - - Alignment - - - - - - - - - - - Transformations: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - TRANSLATION - - - - - RIGID_BODY - - - - - SCALED_ROTATION - - - - - AFFINE - - - - - BILINEAR - - - - - - - - - - - - Reference: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - previous - - - - - mean - - - - - first - - - - - - - - - - - - - 0 - 0 - - - - Iterative Mode - - - - - - - - - Max Iter. - - - - - - - - 0 - 0 - - - - 2 - - - 24 - - - - - - - - - - - - 0 - 0 - - - - Align - - - - - - - - - - 0 - 0 - - - - Load Reference Stack - - - - - - - No Ref. Available - - - true - - - - - - - - - - 0 - 0 - - - - Save Transformation File - - - - - - - Use - - - - - - - - 0 - 0 - - - - Load Transformation File - - - - - - - - - - - - - - - 0 - 0 - - - - - - - Reset Image - - - - - - - true - - - 2 - - - - - 0 - 0 - 379 - 321 - - - - Component Analysis - - - - - - - - - 0 - 0 - - - - - - - Calculate Components - - - - - - - - 0 - 0 - - - - - - - PCA Scree Plot - - - - - - - - - Method - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 0 - 0 - - - - - PCA - - - - - NMF - - - - - FastICA - - - - - IncrementalPCA - - - - - TruncatedSVD - - - - - FactorAnalysis - - - - - DictionaryLearning - - - - - - - - - - - - Number of Components - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - 1 - - - 4 - - - - - - - - - - - - - 0 - 0 - 379 - 321 - - - - Cluster Analysis - - - - - - - - - 0 - 0 - - - - - - - Calculate Clusters - - - - - - - - 0 - 0 - - - - - - - KMeans Variance Plot - - - - - - - Method - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 0 - 0 - - - - - KMeans - - - - - MiniBatchKMeans - - - - - MeanShift - - - - - Spectral Clustering - - - - - Correlation-Kmeans - - - - - Affinity Propagation - - - - - - - - Number of Clusters - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - 1 - - - 4 - - - - - - - - - - - 0 - 0 - 379 - 205 - - - - XANES Fitting - - - - - - - 11 - - - 11 - - - - - - - - 0 - 0 - - - - - - - Load Energy List - - - - - - - keV - - - - - - - - - - - - 0 - 0 - - - - - - - Load Ref. Spec. - - - - - - - - - - Plot - - - - - - - - - true - - - - 0 - 0 - - - - - - - Fit - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - - - - 0 - 0 - - - - - - - - - - - QLayout::SetMaximumSize - - - - - 0 - - - - Live - - - - - - - 0 - 0 - - - - Send to Plot Collection - - - - - - - - 0 - 0 - - - - - - - Save - - - - - - - - 0 - 0 - - - - - - - - - Normalized - - - - - - Save - - - - - - - Clear - - - - - - - Norm. to Collector - - - - - - - - 0 - 0 - - - - - - - - - Collection - - - - - - - 0 - 0 - - - - - - - - Clear - - - - - - - Save - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - 0 - 0 - - - - - - - - - - - - 0 - 0 - - - - QFrame::StyledPanel - - - QFrame::Sunken - - - - QLayout::SetDefaultConstraint - - - 11 - - - 11 - - - - - - 0 - 0 - - - - ROI Shape - - - - - - - 0 - 0 - - - - Rectangle - - - true - - - - - - - - 0 - 0 - - - - Ellipse - - - - - - - - 0 - 0 - - - - Polygon - - - false - - - - - - - - 0 - 0 - - - - Circle - - - - - - - - 0 - 0 - - - - Line - - - - - - - - 0 - 0 - - - - Zoom to ROI - - - - - - - - - - font: 9pt "Segoe UI"; - - - ROI Positions - - - - - - Spectrum ROI - - - - - - Range (eV): - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - color: rgb(255, 0,0); - - - roi_size - - - - - - - color: rgb(255, 0,0); - - - roix, roiy - - - - - - - - - - Size (eV): - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - Image ROI - - - - - - color: rgb(255, 0,0); - - - roix, roiy - - - - - - - Position: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - Size(pixels): - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - color: rgb(255, 0, 0); - - - roi_size - - - - - - - - - - - - - - 0 - 0 - - - - 1 - - - - - 0 - 0 - 361 - 193 - - - - Image Calculations - - - - - - - - - - - - - - - Add ROI_2 - - - - - - - Calculation: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 0 - 0 - - - - - Subtract - - - - - Divide - - - - - Add - - - - - Compare - - - - - - - - Apply - - - - - - - - - - - - 0 - 0 - 361 - 231 - - - - Spectrum Calculations - - - - - - - - - - - - - 0 - 0 - - - - Add ROI 2 - - - true - - - false - - - false - - - - - - - - - - 0 - 0 - - - - Calculation - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 0 - 0 - - - - - Divide - - - - - Subtract - - - - - Add - - - - - Correlation Plot - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Apply - - - - - - - Use ROI for Correlations - - - - - - - - - - - - 0 - 0 - 386 - 273 - - - - XANES Normalization - - - - - - - 0 - 0 - - - - to - - - Qt::AlignCenter - - - - - - - - 0 - 0 - - - - 1 - - - 5 - - - - - - - - 0 - 0 - - - - Eo - - - - - - - - 0 - 0 - - - - eV - - - 1000.000000000000000 - - - 20000.000000000000000 - - - 7125.000000000000000 - - - - - - - - 0 - 0 - - - - calculate the energy point with maximum derivative - - - Auto Eo - - - - - - - - 0 - 0 - - - - eV - - - 2 - - - -500.000000000000000 - - - 500.000000000000000 - - - 1.000000000000000 - - - -50.000000000000000 - - - - - - - - 0 - 0 - - - - Pre-edge - - - - - - - - 0 - 0 - - - - eV - - - -500.000000000000000 - - - 500.000000000000000 - - - -10.000000000000000 - - - - - - - - 0 - 0 - - - - Post-edge - - - - - - - - 0 - 0 - - - - to - - - Qt::AlignCenter - - - - - - - - 0 - 0 - - - - Norm. Order - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 0 - 0 - - - - eV - - - 0.000000000000000 - - - 1000.000000000000000 - - - 25.000000000000000 - - - - - - - - 0 - 0 - - - - <html><head/><body><p> For mbak algorithm only, must be in <span style=" font-weight:600; font-style:italic;">element&lt;space&gt;edge</span> format</p></body></html> - - - Fe K - - - - - - - - 0 - 0 - - - - Element: - - - - - - - - 0 - 0 - - - - eV - - - 0.000000000000000 - - - 1500.000000000000000 - - - 75.000000000000000 - - - - - - - Initial Guess - - - - - - - - 0 - 0 - - - - Apply to Spectrum - - - - - - - Apply to Stack - - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - 0 - 0 - 1232 - 29 - - - - - - - - Help - - - - - - - File - - - - - - - - - - - - true - - - Accessories - - - - - - - View - - - - Plot Background - - - - - - - - - - Window Background - - - - - - - - Change_Plot_Line_Width - - - - - - - - - - - - - - - - - Spectrum - - - - - - - - - Image - - - - - - - - - - - Batch - - - - - - - - - - - - - - - - - 0 - 0 - - - - toolBar - - - TopToolBarArea - - - false - - - - - - - - Open PDF - - - - - Open in GitHub (most updated) - - - - - true - - - Open Image Data - - - Support all tiff and specific h5 files - - - Ctrl+O - - - - - Close - - - - - Exit - - - Ctrl+Q - - - - - false - - - Export Tiff Stack - - - Save the displayed/Modified stack as a tiff file - - - Ctrl+S - - - - - Open PyXRF - - - - - Open Image J - - - - - Open TomViz - - - - - Open Mantis - - - - - Open Athena - - - - - DataBroker - - - - - Open HXN DB - - - - - false - - - Load Energy - - - Load list of energies for XANES stack. Supports only .txt fromat - - - Ctrl+E - - - - - Create a Virtual Stack - - - Create a stack from multiple tiff images of same shape - - - Ctrl+M - - - - - true - - - Open Mask Generator - - - A new window will be opened to creat threshold based masks - - - - - Create elist from log file - - - - - false - - - Export Energy List - - - - - true - - - MultiColorView - - - A new window will be opened to align images in a stack - - - - - White - - - - - true - - - true - - - Black - - - - - Red - - - - - Yellow - - - - - Blue - - - - - false - - - false - - - Dark Mode - - - - - Black - - - - - false - - - false - - - Default - - - - - Vivid - - - - - Export Sum Image (3D to 2D) - - - - - Subtract ROI as Background - - - - - Export Norm. Params - - - - - 2 - - - - - 4 - - - - - 6 - - - - - 10 - - - - - 2 - - - - - 3 - - - - - 4 - - - - - 5 - - - - - 6 - - - - - 8 - - - - - 10 - - - - - 2 - - - - - Import Norm. Params - - - - - 1 - - - - - Save Sum Spectrum - - - - - Save Mean Spectrum - - - - - Save Current Image as Mask - - - - - Stack to RGBCMY Image - - - - - Apply Current Crop to All images - - - all images in the folder will be cropped in reference to current - - - - - - - - - - - Save Current State - - - - - Normalize with another Stack - - - - - Stack Info - - - Diaplays details of the stack - - - - - Export Image - - - Saves displayed 2D image frame - - - - - - .. - - - Export Stack - - - Save current stack as is - - - false - - - - - Export Mean Image (3D to 2D) - - - - - Plot All Possible Image Correlations - - - - - - PlotWidget - QGraphicsView -
pyqtgraph
-
- - ImageView - QGraphicsView -
pyqtgraph
-
-
- - -
+ + + Ajith + MainWindow + + + true + + + + 0 + 0 + 1287 + 842 + + + + + 0 + 0 + + + + NSLS-II MIDAS + + + + pancake.icopancake.ico + + + font: 10pt "Segoe UI"; + + + + + true + + + + 0 + 0 + + + + QWidget { +font: 10pt "Segoe UI"; +} + +QPushButton { +background-color: rgb(175, 236, 255); +color: rgb(255, 5,0); +font: 10pt "Segoe UI"; +} + +QLabel { +font: 10pt "Segoe UI"; +} + +QSlider::groove:horizontal { +border: 1px solid #bbb; +background: white; +height: 10px; +border-radius: 4px; +} + +QSlider::sub-page:horizontal { +background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #66e, stop: 1 #bbf); +background: qlineargradient(x1: 0, y1: 0.2, x2: 1, y2: 1, + stop: 0 #bbf, stop: 1 #55f); +border: 1px solid #777; +height: 10px; +border-radius: 4px; +} + +QSlider::add-page:horizontal { +background: #fff; +border: 1px solid #777; +height: 10px; +border-radius: 4px; +} + +QSlider::handle:horizontal { +background: qlineargradient(x1:0, y1:0, x2:1, y2:1, + stop:0 #eee, stop:1 #ccc); +border: 1px solid #777; +width: 13px; +margin-top: -2px; +margin-bottom: -2px; +border-radius: 4px; +} + +QSlider::handle:horizontal:hover { +background: qlineargradient(x1:0, y1:0, x2:1, y2:1, + stop:0 #fff, stop:1 #ddd); +border: 1px solid #444; +border-radius: 2px; +} + +QSlider::sub-page:horizontal:disabled { +background: #bbb; +border-color: #999; +} + +QSlider::add-page:horizontal:disabled { +background: #eee; +border-color: #999; +} + +QSlider::handle:horizontal:disabled { +background: #eee; +border: 1px solid #aaa; +border-radius: 4px; +} + + + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Sunken + + + + + + + 0 + 0 + + + + + + + 0 + + + + + 0 + 0 + 302 + 288 + + + + Adjust Image Dimensions + + + + + + + 0 + 0 + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + QLayout::SetMinimumSize + + + + + + 0 + 0 + + + + 0 + + + 500 + + + 0 + + + 10 + + + + + + + + 0 + 0 + + + + 1 + + + 5000 + + + 1200 + + + 10 + + + + + + + + 0 + 0 + + + + false + + + px + + + 1 + + + 10000 + + + 100 + + + + + + + + 0 + 0 + + + + false + + + px + + + 1 + + + 10000 + + + 100 + + + + + + + + 0 + 0 + + + + false + + + px + + + 0 + + + 10000 + + + 0 + + + + + + + + 0 + 0 + + + + Y Dimension + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + false + + + px + + + 0 + + + 10000 + + + 0 + + + + + + + + 0 + 0 + + + + X Dimension + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + Stack Range + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + to + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + to + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + to + + + Qt::AlignCenter + + + + + + + + + + 0 + 0 + + + + Adjust the dimensions of the image + + + + + + Update + + + + + + + Apply crop to all tiffs in directory + + + + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + + 0 + 0 + + + + Removes one column/row from all four edges + + + Remove Edges + + + + + + + + + true + + + + 0 + 0 + + + + rebin + + + + + + + true + + + + 0 + 0 + + + + Upscale + + + + + + + + + Ratio + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + true + + + + 0 + 0 + + + + font: 10pt "MS Shell Dlg 2"; + + + 2 + + + 16 + + + 2 + + + 2 + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + 0 + 0 + 233 + 365 + + + + Image Processing + + + + + + + + + reverses the image axes (ZXY) to (YXZ) + + + Transpose + + + + + + + swaps 2nd and 3rd axes (ZXY) to (ZYX) + + + Swap XY Axes + + + + + + + + + + + + 0 + 0 + + + + Covert image dat to log values. + + + + + + Log + + + false + + + + + + + + 0 + 0 + + + + Intensity Normalization with the last Frame. + + + Normalize to Max + + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + Remove Outliers (NSigma) + + + + + + + false + + + + + + 200 + + + 3 + + + Qt::Horizontal + + + + + + + 1 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + + + Thresholding + + + + + + + + + + + false + + + 100 + + + 5 + + + 5 + + + 5 + + + Qt::Horizontal + + + + + + + 5 + + + + + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + uses savgol_filter to smooth data + + + Smoothen + + + + + + + + + false + + + 3 + + + 12 + + + 2 + + + 2 + + + 3 + + + Qt::Horizontal + + + QSlider::TicksBelow + + + 2 + + + + + + + Window size + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + + + + + + + 0 + 0 + 286 + 247 + + + + Alignment + + + + + + + + + + + Transformations: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + TRANSLATION + + + + + RIGID_BODY + + + + + SCALED_ROTATION + + + + + AFFINE + + + + + BILINEAR + + + + + + + + + + + + Reference: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + previous + + + + + mean + + + + + first + + + + + + + + + + + + + 0 + 0 + + + + Iterative Mode + + + + + + + + + Max Iter. + + + + + + + + 0 + 0 + + + + 2 + + + 24 + + + + + + + + + + + + 0 + 0 + + + + Align + + + + + + + + + + 0 + 0 + + + + Load Reference Stack + + + + + + + No Ref. Available + + + true + + + + + + + + + + 0 + 0 + + + + Save Transformation File + + + + + + + Use + + + + + + + + 0 + 0 + + + + Load Transformation File + + + + + + + + + + + + + + + 0 + 0 + + + + + + + Reset Image + + + + + + + true + + + 1 + + + + + 0 + 0 + 302 + 205 + + + + Component Analysis + + + + + + + + + 0 + 0 + + + + + + + Calculate Components + + + + + + + + 0 + 0 + + + + + + + PCA Scree Plot + + + + + + + + + Method + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + + PCA + + + + + NMF + + + + + FastICA + + + + + IncrementalPCA + + + + + TruncatedSVD + + + + + FactorAnalysis + + + + + DictionaryLearning + + + + + + + + + + + + Number of Components + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + 1 + + + 4 + + + + + + + + + + + + + 0 + 0 + 302 + 205 + + + + Cluster Analysis + + + + + + + + + 0 + 0 + + + + + + + Calculate Clusters + + + + + + + + 0 + 0 + + + + + + + KMeans Variance Plot + + + + + + + Method + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + + KMeans + + + + + MiniBatchKMeans + + + + + MeanShift + + + + + Spectral Clustering + + + + + Correlation-Kmeans + + + + + Affinity Propagation + + + + + + + + Number of Clusters + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + 1 + + + 4 + + + + + + + + + + + 0 + 0 + 302 + 205 + + + + XANES Fitting + + + + + + + 11 + + + 11 + + + + + + + + 0 + 0 + + + + + + + Load Energy List + + + + + + + keV + + + + + + + + + + + + 0 + 0 + + + + + + + Load Ref. Spec. + + + + + + + + + + Plot + + + + + + + + + true + + + + 0 + 0 + + + + + + + Fit + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + + 0 + 0 + + + + + + + + + + + QLayout::SetMaximumSize + + + + + 0 + + + + Live + + + + + + + 0 + 0 + + + + Send to Plot Collection + + + + + + + + 0 + 0 + + + + + + + Save + + + + + + + + 0 + 0 + + + + + + + + + Normalized + + + + + + + 0 + 0 + + + + + + + + Norm. to Collector + + + + + + + Save + + + + + + + Clear + + + + + + + + Collection + + + + + + + 0 + 0 + + + + + + + + Clear + + + + + + + Save + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + 0 + 0 + + + + + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Sunken + + + + + + + + font: 9pt "Segoe UI"; + + + ROI Positions + + + + + + Spectrum ROI + + + + + + Range (eV): + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + color: rgb(255, 0,0); + + + roi_size + + + + + + + color: rgb(255, 0,0); + + + roix, roiy + + + + + + + + + + Size (eV): + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Image ROI + + + + + + Position: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + color: rgb(255, 0,0); + + + roix, roiy + + + + + + + + + + Size(pixels): + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + color: rgb(255, 0, 0); + + + roi_size + + + + + + + + + + + + + + 0 + 0 + + + + ROI Shape + + + + + + + 0 + 0 + + + + Ellipse + + + + + + + + 0 + 0 + + + + Rectangle + + + true + + + + + + + + 0 + 0 + + + + Polygon + + + false + + + + + + + + 0 + 0 + + + + Circle + + + + + + + + 0 + 0 + + + + Zoom to ROI + + + + + + + + 0 + 0 + + + + Line + + + + + + + + + + + 0 + 0 + + + + Qt::LeftToRight + + + 2 + + + + + 0 + 0 + 305 + 384 + + + + Image Calculations + + + + + + + + + + + + + + + Add ROI_2 + + + + + + + Calculation: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + + Subtract + + + + + Divide + + + + + Add + + + + + Compare + + + + + + + + Apply + + + + + + + + + + + + 0 + 0 + 323 + 384 + + + + Spectrum Calculations + + + + + + + + + + + + + 0 + 0 + + + + Add ROI 2 + + + true + + + false + + + false + + + + + + + + + + 0 + 0 + + + + Calculation + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + + Divide + + + + + Subtract + + + + + Add + + + + + Correlation Plot + + + + + + + + + + Use ROI for Correlations + + + + + + + Apply + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + 0 + 0 + 365 + 384 + + + + XANES Normalization + + + + + + + + + + + + + + + 0 + 0 + + + + calculate the energy point with maximum derivative + + + Find Eo + + + + + + + + 0 + 0 + + + + Initial Guess + + + + + + + + + + + + 0 + 0 + + + + Element_Edge: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + Pre-edge + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + Post-edge + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + + 0 + 0 + + + + <html><head/><body><p> For mbak algorithm only, must be in <span style=" font-weight:600; font-style:italic;">element&lt;space&gt;edge</span> format</p></body></html> + + + Fe_K + + + + + + + + 0 + 0 + + + + eV + + + 2 + + + -500.000000000000000 + + + 500.000000000000000 + + + 1.000000000000000 + + + -50.000000000000000 + + + + + + + + 0 + 0 + + + + eV + + + 0.000000000000000 + + + 1000.000000000000000 + + + 25.000000000000000 + + + + + + + + + + + + 0 + 0 + + + + Eo: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + to + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + to + + + Qt::AlignCenter + + + + + + + + + + + + 0 + 0 + + + + eV + + + 1000.000000000000000 + + + 20000.000000000000000 + + + 7125.000000000000000 + + + + + + + + 0 + 0 + + + + eV + + + -500.000000000000000 + + + 500.000000000000000 + + + -10.000000000000000 + + + + + + + + 0 + 0 + + + + eV + + + 0.000000000000000 + + + 1500.000000000000000 + + + 75.000000000000000 + + + + + + + + + + + + + + + + 0 + 0 + + + + Norm. Order + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + 1 + + + 5 + + + + + + + + + + 0 + 0 + + + + Use Flattened + + + + + + + + 0 + 0 + + + + Apply to Spectrum + + + + + + + + 0 + 0 + + + + Apply to Stack + + + + + + + + 0 + 0 + + + + Ignore Post Edge (Stack Only) + + + + + + + MBAK Algorithm + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 1287 + 24 + + + + + + + + Help + + + + + + + File + + + + + + + + + + + + true + + + Accessories + + + + + + + View + + + + Plot Background + + + + + + + + + + Window Background + + + + + + + + Change_Plot_Line_Width + + + + + + + + + + + + + + + + + Spectrum + + + + + + + + + Image + + + + + + + + + + + + Batch + + + + + + + + + + + + + + + + + 0 + 0 + + + + toolBar + + + TopToolBarArea + + + false + + + + + + + + Open PDF + + + + + Open in GitHub (most updated) + + + + + true + + + Open Image Data + + + Support all tiff and specific h5 files + + + Ctrl+O + + + + + Close + + + + + Exit + + + Ctrl+Q + + + + + false + + + Export Tiff Stack + + + Save the displayed/Modified stack as a tiff file + + + Ctrl+S + + + + + Open PyXRF + + + + + Open Image J + + + + + Open TomViz + + + + + Open Mantis + + + + + Open Athena + + + + + DataBroker + + + + + Open HXN DB + + + + + false + + + Load Energy + + + Load list of energies for XANES stack. Supports only .txt fromat + + + Ctrl+E + + + + + Create a Virtual Stack + + + Create a stack from multiple tiff images of same shape + + + Ctrl+M + + + + + true + + + Open Mask Generator + + + A new window will be opened to creat threshold based masks + + + + + Create elist from log file + + + + + false + + + Export Energy List + + + + + true + + + MultiColorView + + + A new window will be opened to align images in a stack + + + + + White + + + + + true + + + true + + + Black + + + + + Red + + + + + Yellow + + + + + Blue + + + + + false + + + false + + + Dark Mode + + + + + Black + + + + + false + + + false + + + Default + + + + + Vivid + + + + + Export Sum Image (XYZ to sum(XY)) + + + + + Subtract ROI as Background + + + + + Export Norm. Params + + + + + 2 + + + + + 4 + + + + + 6 + + + + + 10 + + + + + 2 + + + + + 3 + + + + + 4 + + + + + 5 + + + + + 6 + + + + + 8 + + + + + 10 + + + + + 2 + + + + + Import Norm. Params + + + + + 1 + + + + + Save Sum Spectrum + + + + + Save Mean Spectrum + + + + + Save Current Image as Mask + + + + + Stack to RGBCMY Image + + + + + Apply Current Crop to All images + + + all images in the folder will be cropped in reference to current + + + + + + + + + + + Save Current State + + + + + Normalize with another Stack + + + + + Stack Info + + + Diaplays details of the stack + + + + + Export Image + + + Saves displayed 2D image frame + + + + + + .. + + + Export Stack + + + Save current stack as is + + + false + + + + + Export Mean Image (XYZ to mean(XY)) + + + + + Plot All Possible Image Correlations + + + + + Export Image as CSV (XYZ to Z,X*Y) + + + + + + PlotWidget + QGraphicsView +
pyqtgraph
+
+ + ImageView + QGraphicsView +
pyqtgraph
+
+
+ + +
diff --git a/xmidas/uis/multipleScatterFit.ui b/xmidas/uis/multipleScatterFit.ui index 7112364..64b9363 100644 --- a/xmidas/uis/multipleScatterFit.ui +++ b/xmidas/uis/multipleScatterFit.ui @@ -1,114 +1,114 @@ - - - MainWindow - - - - 0 - 0 - 856 - 628 - - - - MainWindow - - - - - - - - - - - - - - 0 - 0 - - - - - - - - - - - - - 0 - 0 - 856 - 21 - - - - - View - - - - Plot Background - - - - - - - - - - - - - toolBar - - - TopToolBarArea - - - false - - - - - - - - Export - - - - - Save as PNG - - - - - Generate MultiColor Mask - - - - - Black - - - - - White - - - - - - GraphicsLayoutWidget - QGraphicsView -
pyqtgraph
-
-
- - -
+ + + MainWindow + + + + 0 + 0 + 856 + 628 + + + + MainWindow + + + + + + + + + + + + + + 0 + 0 + + + + + + + + + + + + + 0 + 0 + 856 + 21 + + + + + View + + + + Plot Background + + + + + + + + + + + + + toolBar + + + TopToolBarArea + + + false + + + + + + + + Export + + + + + Save as PNG + + + + + Generate MultiColor Mask + + + + + Black + + + + + White + + + + + + GraphicsLayoutWidget + QGraphicsView +
pyqtgraph
+
+
+ + +
diff --git a/xmidas/uis/mutlichannel.ui b/xmidas/uis/mutlichannel.ui index 133f006..7315290 100644 --- a/xmidas/uis/mutlichannel.ui +++ b/xmidas/uis/mutlichannel.ui @@ -1,458 +1,464 @@ - - - MainWindow - - - - 0 - 0 - 674 - 765 - - - - MainWindow - - - font: 12pt "Segoe UI" - - - - QPushButton { -border: 1px solid #555; -border-radius: 5px; -background: qradialgradient(cx: 0.3, cy: -0.1, -fx: 0.7, fy: 0.1, -radius: 1, stop: 0 #fff, stop: 1 #888); -background-color: rgb(170, 255, 255); -} - -QPushButton:hover{ - background-color: rgb(255, 255, 0); - } - -QPushButton:pressed{ - background-color: rgb(0,255, 0); - } - -font: 12pt "Segoe UI"; - - - - 25 - - - 25 - - - 25 - - - 25 - - - - - - 0 - 0 - - - - - - - - Edit - - - - - - - 0 - 0 - - - - 0 - - - - - 0 - 0 - 316 - 85 - - - - Thresholding - - - - - - - - - 0 - 0 - - - - 0,100 - - - Qt::AlignCenter - - - - - - - - - - - - 0 - 0 - - - - Qt::NoFocus - - - click update after making changes - - - 100 - - - 5 - - - 5 - - - 100 - - - 100 - - - true - - - Qt::Horizontal - - - false - - - false - - - QSlider::NoTicks - - - 5 - - - - - - - - - - - - 0 - 0 - - - - Qt::NoFocus - - - click update after making changes - - - 100 - - - 5 - - - 5 - - - 0 - - - 0 - - - true - - - Qt::Horizontal - - - QSlider::NoTicks - - - 5 - - - - - - - - - - - - - - - 0 - 0 - 316 - 85 - - - - Opacity - - - - - - - - - 0 - 0 - - - - 1 - - - Qt::AlignCenter - - - - - - - click update after making changes - - - 100 - - - 10 - - - 100 - - - Qt::Horizontal - - - - - - - - - - - - - execute above changes to the selected item - - - Update - - - - - - - - - Show Selected - - - - - - - Show All - - - - - - - - - - 0 - 0 - - - - change properties of the selected item - - - font: 8pt "Segoe UI"; - - - QAbstractScrollArea::AdjustToContents - - - - - - - - - - 0 - 0 - - - - Change Selected To - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - true - - - - - - - - - - - - - - - - - - 0 - 0 - 674 - 34 - - - - - File - - - true - - - - - - - - - - - - - Load Images - - - Select and load multiple tiff images to create a multi color view - - - - - Load 2 - - - - - Load 3 - - - - - Load 4 - - - - - Load 5 - - - - - Load 6 - - - - - Export Image - - - Export the image view as a sinle image file - - - - - Save State File - - - Save the current state of the view. Images and properties are saved - - - - - Load State File - - - Load a state (json file) saved previously. - - - - - Load Stack - - - Load images as a stack of tiff - - - - - - GraphicsLayoutWidget - QGraphicsView -
pyqtgraph
-
-
- - -
+ + + MainWindow + + + + 0 + 0 + 612 + 765 + + + + MainWindow + + + font: 12pt "Segoe UI" + + + + QPushButton { +border: 1px solid #555; +border-radius: 5px; +background: qradialgradient(cx: 0.3, cy: -0.1, +fx: 0.7, fy: 0.1, +radius: 1, stop: 0 #fff, stop: 1 #888); +background-color: rgb(170, 255, 255); +} + +QPushButton:hover{ + background-color: rgb(255, 255, 0); + } + +QPushButton:pressed{ + background-color: rgb(0,255, 0); + } + +font: 12pt "Segoe UI"; + + + + 25 + + + 25 + + + 25 + + + 25 + + + + + + 0 + 0 + + + + + + + + Edit + + + + + + + 0 + 0 + + + + 0 + + + + + 0 + 0 + 254 + 85 + + + + Thresholding + + + + + + + + + 0 + 0 + + + + 0,100 + + + Qt::AlignCenter + + + + + + + + + + + + 0 + 0 + + + + Qt::NoFocus + + + click update after making changes + + + 100 + + + 5 + + + 5 + + + 100 + + + 100 + + + true + + + Qt::Horizontal + + + false + + + false + + + QSlider::NoTicks + + + 5 + + + + + + + + + + + + 0 + 0 + + + + Qt::NoFocus + + + click update after making changes + + + 100 + + + 5 + + + 5 + + + 0 + + + 0 + + + true + + + Qt::Horizontal + + + QSlider::NoTicks + + + 5 + + + + + + + + + + + + + + + 0 + 0 + 98 + 81 + + + + Opacity + + + + + + + + + 0 + 0 + + + + 1 + + + Qt::AlignCenter + + + + + + + click update after making changes + + + 100 + + + 10 + + + 100 + + + Qt::Horizontal + + + + + + + + + + + + + execute above changes to the selected item + + + Update + + + + + + + + + Show Selected + + + + + + + Show All + + + + + + + + + + 0 + 0 + + + + change properties of the selected item + + + font: 8pt "Segoe UI"; + + + QAbstractScrollArea::AdjustToContents + + + + + + + + + + 0 + 0 + + + + Change Selected To + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + true + + + + + + + + + + + + + + + + + + 0 + 0 + 612 + 34 + + + + + File + + + true + + + + + + + + + + + + + + Load Images + + + Select and load multiple tiff images to create a multi color view + + + + + Load 2 + + + + + Load 3 + + + + + Load 4 + + + + + Load 5 + + + + + Load 6 + + + + + Export Image + + + Export the image view as a sinle image file + + + + + Save State File + + + Save the current state of the view. Images and properties are saved + + + + + Load State File + + + Load a state (json file) saved previously. + + + + + Load Stack + + + Load images as a stack of tiff + + + + + Save Stack (.tiff) + + + + + + GraphicsLayoutWidget + QGraphicsView +
pyqtgraph
+
+
+ + +
diff --git a/xmidas/uis/singleStackView.ui b/xmidas/uis/singleStackView.ui index d832b04..22ee549 100644 --- a/xmidas/uis/singleStackView.ui +++ b/xmidas/uis/singleStackView.ui @@ -1,151 +1,151 @@ - - - MainWindow - - - - 0 - 0 - 691 - 566 - - - - MainWindow - - - QSlider::groove:horizontal { -border: 1px solid #bbb; -background: white; -height: 10px; -border-radius: 4px; -} - -QSlider::sub-page:horizontal { -background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #66e, stop: 1 #bbf); -background: qlineargradient(x1: 0, y1: 0.2, x2: 1, y2: 1, - stop: 0 #bbf, stop: 1 #55f); -border: 1px solid #777; -height: 10px; -border-radius: 4px; -} - -QSlider::add-page:horizontal { -background: #fff; -border: 1px solid #777; -height: 10px; -border-radius: 4px; -} - -QSlider::handle:horizontal { -background: qlineargradient(x1:0, y1:0, x2:1, y2:1, - stop:0 #eee, stop:1 #ccc); -border: 1px solid #777; -width: 13px; -margin-top: -2px; -margin-bottom: -2px; -border-radius: 4px; -} - -QSlider::handle:horizontal:hover { -background: qlineargradient(x1:0, y1:0, x2:1, y2:1, - stop:0 #fff, stop:1 #ddd); -border: 1px solid #444; -border-radius: 2px; -} - -QSlider::sub-page:horizontal:disabled { -background: #bbb; -border-color: #999; -} - -QSlider::add-page:horizontal:disabled { -background: #eee; -border-color: #999; -} - -QSlider::handle:horizontal:disabled { -background: #eee; -border: 1px solid #aaa; -border-radius: 4px; -} - -QPushButton { -background-color: rgb(175, 236, 255); -color: rgb(255, 0, 127); -} - - - - - - - - - - 0 - 0 - - - - - - - - - - Qt::Horizontal - - - - - - - font: 12pt "MS Shell Dlg 2"; - - - 10 - - - - - - - - - - - - - 0 - 0 - 691 - 26 - - - - - File - - - - - - - - - Save - - - - - - ImageView - QGraphicsView -
pyqtgraph
-
-
- - -
+ + + MainWindow + + + + 0 + 0 + 691 + 566 + + + + MainWindow + + + QSlider::groove:horizontal { +border: 1px solid #bbb; +background: white; +height: 10px; +border-radius: 4px; +} + +QSlider::sub-page:horizontal { +background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #66e, stop: 1 #bbf); +background: qlineargradient(x1: 0, y1: 0.2, x2: 1, y2: 1, + stop: 0 #bbf, stop: 1 #55f); +border: 1px solid #777; +height: 10px; +border-radius: 4px; +} + +QSlider::add-page:horizontal { +background: #fff; +border: 1px solid #777; +height: 10px; +border-radius: 4px; +} + +QSlider::handle:horizontal { +background: qlineargradient(x1:0, y1:0, x2:1, y2:1, + stop:0 #eee, stop:1 #ccc); +border: 1px solid #777; +width: 13px; +margin-top: -2px; +margin-bottom: -2px; +border-radius: 4px; +} + +QSlider::handle:horizontal:hover { +background: qlineargradient(x1:0, y1:0, x2:1, y2:1, + stop:0 #fff, stop:1 #ddd); +border: 1px solid #444; +border-radius: 2px; +} + +QSlider::sub-page:horizontal:disabled { +background: #bbb; +border-color: #999; +} + +QSlider::add-page:horizontal:disabled { +background: #eee; +border-color: #999; +} + +QSlider::handle:horizontal:disabled { +background: #eee; +border: 1px solid #aaa; +border-radius: 4px; +} + +QPushButton { +background-color: rgb(175, 236, 255); +color: rgb(255, 0, 127); +} + + + + + + + + + + 0 + 0 + + + + + + + + + + Qt::Horizontal + + + + + + + font: 12pt "MS Shell Dlg 2"; + + + 10 + + + + + + + + + + + + + 0 + 0 + 691 + 26 + + + + + File + + + + + + + + + Save + + + + + + ImageView + QGraphicsView +
pyqtgraph
+
+
+ + +
diff --git a/xmidas/uis/xanesFitStat.ui b/xmidas/uis/xanesFitStat.ui index 25685bf..258e475 100644 --- a/xmidas/uis/xanesFitStat.ui +++ b/xmidas/uis/xanesFitStat.ui @@ -1,47 +1,47 @@ - - - MainWindow - - - - 0 - 0 - 792 - 481 - - - - MainWindow - - - - - - - - - - - - - - - 0 - 0 - 792 - 26 - - - - - - - - PlotWidget - QGraphicsView -
pyqtgraph
-
-
- - -
+ + + MainWindow + + + + 0 + 0 + 792 + 481 + + + + MainWindow + + + + + + + + + + + + + + + 0 + 0 + 792 + 26 + + + + + + + + PlotWidget + QGraphicsView +
pyqtgraph
+
+
+ + +
diff --git a/xmidas/uis/xrf_xanes_gui_3ID.ui b/xmidas/uis/xrf_xanes_gui_3ID.ui index 8755841..18a9043 100644 --- a/xmidas/uis/xrf_xanes_gui_3ID.ui +++ b/xmidas/uis/xrf_xanes_gui_3ID.ui @@ -1,756 +1,756 @@ - - - Ajith - MainWindow - - - - 0 - 0 - 820 - 553 - - - - HXN_Wizard - - - font: 12pt "MS Shell Dlg 2"; - - - - - - - - - - 0 - 0 - - - - - - - - 20 - - - - - - - - 0 - 0 - - - - - - - C:\Matrix\Blue Pill\Morpheus.csv - - - - - - - - 0 - 0 - - - - - - - C:\Matrix\Blue Pill - - - - - - - 0 - - - 10 - - - - - - 0 - 0 - - - - Select Working Directory - - - - - - - - 0 - 0 - - - - Select Paramater File - - - - - - - - 0 - 0 - - - - Select XANES Reference File (xanes only) - - - - - - - - - - 0 - 0 - - - - - - - C:\Matrix\Blue Pill\TheChosenOne.json - - - - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 0 - 0 - - - - background-color: rgb(255, 221, 98); - - - Open PyXRF GUI - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - font: 12pt "MS Shell Dlg 2"; - - - 1 - - - false - - - - XRF - - - - - - XRF Batch Processing - - - Qt::AlignCenter - - - - - - 150 - - - 150 - - - - - 92158 - - - 00000 - - - - - - - 92102 - - - 00000 - - - - - - - Last Scan ID: - - - - - - - Scalar Name: - - - - - - - First Scan ID: - - - - - - - sclr1_ch4 - - - Fe_K - - - - - - - background-color: rgb(85, 255, 127); - - - Start Batch Processing - - - - - - - - - - - - 150 - - - 150 - - - - - XRF Live Processing - - - Qt::AlignCenter - - - - - - - - background-color: rgb(0, 170, 255); -background-color: rgb(67, 246, 255); - - - Initiate Live - - - - - - - background-color: rgb(255, 130, 67); -font: 75 12pt "MS Shell Dlg 2"; - - - - Start - - - - - - - Live Processing is not ready - - - Qt::AlignCenter - - - - - - - - - - - - - - - XANES - - - - - - 50 - - - 50 - - - - - - - First Scan ID: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - 92102 - - - 00000 - - - - - - - XANES Element - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Fe_K - - - Fe_K - - - - - - - Alignemnt Element - - - - - - - P_K - - - Pt_L - - - - - - - - - 50 - - - 0 - - - - - Last Scan ID: - - - - - - - 92158 - - - 00000 - - - - - - - Scalar Name - - - - - - - sclr1_ch4 - - - Fe_K - - - - - - - Save All Elem tiff Stacks - - - true - - - - - - - - 0 - 0 - - - - Subtract Pre-edge - - - - - - - - - - - Qt::Horizontal - - - - 49 - 20 - - - - - - - - Qt::Vertical - - - - 20 - 48 - - - - - - - - 20 - - - 20 - - - - - - 0 - 0 - - - - Fitting method - - - - - - - - 0 - 0 - - - - - nnls - - - - - admm - - - - - - - - false - - - - 0 - 0 - - - - Lambda for ADMM: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - false - - - - 0 - 0 - - - - Qt::StrongFocus - - - 5 - - - Lambda for ADMM - - - - - - - - 0 - 0 - - - - Energy Shift (eV) - - - - - - - - 0 - 0 - - - - Qt::StrongFocus - - - 0 - - - Lambda for ADMM - - - - - - - Work Flow - - - - - - - - 0 - 0 - - - - - load_and_process - - - - - process - - - - - build_xanes_map - - - - - - - - font: 12pt "MS Shell Dlg 2"; -background-color: rgb(170, 255, 255); - - - Go - - - - - - - background-color: rgb(255, 77, 46); -color: rgb(0, 0, 0); -font: 75 10pt "MS Shell Dlg 2"; - - - Close All Plots - - - - - - - - - Qt::Horizontal - - - - 49 - 20 - - - - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Qt::Vertical - - - - 20 - 52 - - - - - - - - - - 0 - 0 - 820 - 25 - - - - - HELP - - - - - - - - - + + + Ajith + MainWindow + + + + 0 + 0 + 820 + 553 + + + + HXN_Wizard + + + font: 12pt "MS Shell Dlg 2"; + + + + + + + + + + 0 + 0 + + + + + + + + 20 + + + + + + + + 0 + 0 + + + + + + + C:\Matrix\Blue Pill\Morpheus.csv + + + + + + + + 0 + 0 + + + + + + + C:\Matrix\Blue Pill + + + + + + + 0 + + + 10 + + + + + + 0 + 0 + + + + Select Working Directory + + + + + + + + 0 + 0 + + + + Select Paramater File + + + + + + + + 0 + 0 + + + + Select XANES Reference File (xanes only) + + + + + + + + + + 0 + 0 + + + + + + + C:\Matrix\Blue Pill\TheChosenOne.json + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + background-color: rgb(255, 221, 98); + + + Open PyXRF GUI + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + font: 12pt "MS Shell Dlg 2"; + + + 1 + + + false + + + + XRF + + + + + + XRF Batch Processing + + + Qt::AlignCenter + + + + + + 150 + + + 150 + + + + + 92158 + + + 00000 + + + + + + + 92102 + + + 00000 + + + + + + + Last Scan ID: + + + + + + + Scalar Name: + + + + + + + First Scan ID: + + + + + + + sclr1_ch4 + + + Fe_K + + + + + + + background-color: rgb(85, 255, 127); + + + Start Batch Processing + + + + + + + + + + + + 150 + + + 150 + + + + + XRF Live Processing + + + Qt::AlignCenter + + + + + + + + background-color: rgb(0, 170, 255); +background-color: rgb(67, 246, 255); + + + Initiate Live + + + + + + + background-color: rgb(255, 130, 67); +font: 75 12pt "MS Shell Dlg 2"; + + + + Start + + + + + + + Live Processing is not ready + + + Qt::AlignCenter + + + + + + + + + + + + + + + XANES + + + + + + 50 + + + 50 + + + + + + + First Scan ID: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + 92102 + + + 00000 + + + + + + + XANES Element + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Fe_K + + + Fe_K + + + + + + + Alignemnt Element + + + + + + + P_K + + + Pt_L + + + + + + + + + 50 + + + 0 + + + + + Last Scan ID: + + + + + + + 92158 + + + 00000 + + + + + + + Scalar Name + + + + + + + sclr1_ch4 + + + Fe_K + + + + + + + Save All Elem tiff Stacks + + + true + + + + + + + + 0 + 0 + + + + Subtract Pre-edge + + + + + + + + + + + Qt::Horizontal + + + + 49 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 48 + + + + + + + + 20 + + + 20 + + + + + + 0 + 0 + + + + Fitting method + + + + + + + + 0 + 0 + + + + + nnls + + + + + admm + + + + + + + + false + + + + 0 + 0 + + + + Lambda for ADMM: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + false + + + + 0 + 0 + + + + Qt::StrongFocus + + + 5 + + + Lambda for ADMM + + + + + + + + 0 + 0 + + + + Energy Shift (eV) + + + + + + + + 0 + 0 + + + + Qt::StrongFocus + + + 0 + + + Lambda for ADMM + + + + + + + Work Flow + + + + + + + + 0 + 0 + + + + + load_and_process + + + + + process + + + + + build_xanes_map + + + + + + + + font: 12pt "MS Shell Dlg 2"; +background-color: rgb(170, 255, 255); + + + Go + + + + + + + background-color: rgb(255, 77, 46); +color: rgb(0, 0, 0); +font: 75 10pt "MS Shell Dlg 2"; + + + Close All Plots + + + + + + + + + Qt::Horizontal + + + + 49 + 20 + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 52 + + + + + + + + + + 0 + 0 + 820 + 25 + + + + + HELP + + + + + + + + + diff --git a/xmidas/utils.py b/xmidas/utils.py new file mode 100644 index 0000000..31067ac --- /dev/null +++ b/xmidas/utils.py @@ -0,0 +1,815 @@ +""" Helper Functions (make a class later)""" + + +import h5py +import logging +import numpy as np +import pandas as pd +import os +import scipy.optimize as opt +import scipy.stats as stats + +import sklearn.decomposition as sd +import sklearn.cluster as sc +from scipy.signal import savgol_filter +from skimage.transform import resize +from skimage import filters +from sklearn import linear_model +from larch.xafs import pre_edge, preedge, mback +from larch.io import read_ascii, read_athena +from larch import Group +import xraydb +from pystackreg import StackReg + +logger = logging.getLogger() + +def get_xrf_data(h='h5file'): + """ + get xrf stack from h5 data generated at NSLS-II beamlines + + Arguments: + h5/hdf5 file + + Returns: + norm_xrf_stack - xrf stack image normalized with Io + mono_e - excitation enegy used for xrf + beamline - identity of the beamline + Io_avg - an average Io value, used before taking log + + """ + + f = h5py.File(h, "r") + + if list(f.keys())[0] == "xrfmap": + logger.info("Data from HXN/TES/SRX") + beamline = f["xrfmap/scan_metadata"].attrs["scan_instrument_id"] + + try: + + beamline_scalar = {"HXN": 2, "SRX": 0, "TES": 0} + + if beamline in beamline_scalar.keys(): + + Io = np.array(f["xrfmap/scalers/val"])[:, :, beamline_scalar[beamline]] + raw_xrf_stack = np.array(f["xrfmap/detsum/counts"]) + norm_xrf_stack = raw_xrf_stack + Io_avg = int(remove_nan_inf(Io).mean()) + else: + logger.error("Unknown Beamline Scalar") + except Exception: + logger.warning("Unknown Scalar: Raw Detector count in use") + norm_xrf_stack = np.array(f["xrfmap/detsum/counts"]) + + elif list(f.keys())[0] == "xrmmap": + logger.info("Data from XFM") + beamline = "XFM" + raw_xrf_stack = np.array(f["xrmmap/mcasum/counts"]) + Io = np.array(f["xrmmap/scalars/I0"]) + norm_xrf_stack = raw_xrf_stack + Io_avg = int(remove_nan_inf(Io).mean()) + + elif list(f.keys())[0] == "MAPS": + logger.info("MAPS file") + beamline = "APS" + raw_xrf_stack = np.array(f["MAPS/Spectra/mca_arr"]) + Io = 1 #have to find the name of the scalar + norm_xrf_stack = raw_xrf_stack.transpose((1, 2, 0)) + Io_avg = int(remove_nan_inf(Io).mean()) + + else: + logger.error("Unknown Data Format") + + try: + mono_e = int(f["xrfmap/scan_metadata"].attrs["instrument_mono_incident_energy"] * 1000) + logger.info("Excitation energy was taken from the h5 data") + + except Exception: + mono_e = 12000 + logger.info(f"Unable to get Excitation energy from the h5 data; using default value {mono_e} KeV") + + return remove_nan_inf(norm_xrf_stack.transpose((2, 0, 1))), mono_e + 1500, beamline, Io_avg + + +def remove_nan_inf(im): + im = np.array(im, dtype=np.float32) + im[np.isnan(im)] = 0 + im[np.isinf(im)] = 0 + return im + + +def rebin_image(im, bin_factor): + arrx, arry = np.shape(im) + if arrx / bin_factor != int or arrx / bin_factor != int: + logger.error("Invalid Binning") + + else: + shape = (arrx / bin_factor, arry / bin_factor) + return im.reshape(shape).mean(-1).mean(1) + + +def remove_hot_pixels(image_array, NSigma=5): + image_array = remove_nan_inf(image_array) + a, b, c = np.shape(image_array) + img_stack2 = np.zeros((a, b, c)) + for i in range(a): + im = image_array[i, :, :] + im[abs(im) > np.std(im) * NSigma] = im.mean() + img_stack2[i, :, :] = im + return img_stack2 + + +def smoothen(image_array, w_size=5): + a, b, c = np.shape(image_array) + spec2D_Matrix = np.reshape(image_array, (a, (b * c))) + smooth2D_Matrix = savgol_filter(spec2D_Matrix, w_size, w_size - 2, axis=0) + return remove_nan_inf(np.reshape(smooth2D_Matrix, (a, b, c))) + + +def resize_stack(image_array, upscaling=False, scaling_factor=2): + en, im1, im2 = np.shape(image_array) + + if upscaling: + im1_ = im1 * scaling_factor + im2_ = im2 * scaling_factor + img_stack_resized = resize(image_array, (en, im1_, im2_)) + + else: + im1_ = int(im1 / scaling_factor) + im2_ = int(im2 / scaling_factor) + img_stack_resized = resize(image_array, (en, im1_, im2_)) + + return img_stack_resized + + +def normalize(image_array, norm_point=-1): + norm_stack = image_array / image_array[norm_point] + return remove_nan_inf(norm_stack) + + +def remove_edges(image_array): + # z, x, y = np.shape(image_array) + return image_array[:, 1:-1, 1:-1] + + +def background_value(image_array): + img = image_array.mean(0) + img_h = img.mean(0) + img_v = img.mean(1) + h = np.gradient(img_h) + v = np.gradient(img_v) + bg = np.min([img_h[h == h.max()], img_v[v == v.max()]]) + return bg + + +def background_subtraction(img_stack, bg_percentage=10): + img_stack = remove_nan_inf(img_stack) + a, b, c = np.shape(img_stack) + ref_image = np.reshape(img_stack.mean(0), (b * c)) + bg_ratio = int((b * c) * 0.01 * bg_percentage) + bg_ = np.max(sorted(ref_image)[0:bg_ratio]) + bged_img_stack = img_stack - bg_[:, np.newaxis, np.newaxis] + return bged_img_stack + + +def background_subtraction2(img_stack, bg_percentage=10): + img_stack = remove_nan_inf(img_stack) + a, b, c = np.shape(img_stack) + bg_ratio = int((b * c) * 0.01 * bg_percentage) + bged_img_stack = img_stack.copy() + + for n, img in enumerate(img_stack): + bg_ = np.max(sorted(img.flatten())[0:bg_ratio]) + print(bg_) + bged_img_stack[n] = img - bg_ + + return remove_nan_inf(bged_img_stack) + + +def background1(img_stack): + img = img_stack.sum(0) + img_h = img.mean(0) + img_v = img.mean(1) + h = np.gradient(img_h) + v = np.gradient(img_v) + bg = np.min([img_h[h == h.max()], img_v[v == v.max()]]) + return bg + + +def get_sum_spectra(image_array): + spec = np.sum(image_array, axis=(1, 2)) + return spec + + +def get_mean_spectra(image_array): + spec = np.mean(image_array, axis=(1, 2)) + return spec + + +def flatten_(image_array): + z, x, y = np.shape(image_array) + flat_array = np.reshape(image_array, (x * y, z)) + return flat_array + + +def image_to_pandas(image_array): + a, b, c = np.shape(image_array) + im_array = np.reshape(image_array, ((b * c), a)) + a, b = im_array.shape + df = pd.DataFrame( + data=im_array[:, :], columns=["e" + str(i) for i in range(b)], index=["s" + str(i) for i in range(a)] + ) + return df + + +def image_to_pandas2(image_array): + a, b, c = np.shape(image_array) + im_array = np.reshape(image_array, (a, (b * c))) + a, b = im_array.shape + df = pd.DataFrame( + data=im_array[:, :], index=["e" + str(i) for i in range(a)], columns=["s" + str(i) for i in range(b)] + ) + return df + + +def neg_log(image_array): + absorb = -1 * np.log(image_array) + return remove_nan_inf(absorb) + + +def clean_stack(img_stack, auto_bg=False, bg_percentage=5): + a, b, c = np.shape(img_stack) + + if auto_bg is True: + bg_ = background1(img_stack) + + else: + sum_spec = (img_stack.sum(1)).sum(1) + ref_stk_num = np.where(sum_spec == sum_spec.max())[-1] + + ref_image = np.reshape(img_stack[ref_stk_num], (b * c)) + bg_ratio = int((b * c) * 0.01 * bg_percentage) + bg_ = np.max(sorted(ref_image)[0:bg_ratio]) + + bg = np.where(img_stack[ref_stk_num] > bg_, img_stack[ref_stk_num], 0) + bg2 = np.where(bg < bg_, bg, 1) + + bged_img_stack = img_stack * bg2 + + return remove_nan_inf(bged_img_stack) + + +def subtractBackground(im_stack, bg_region): + if bg_region.ndim == 3: + bg_region_ = np.mean(bg_region, axis=(1, 2)) + + elif bg_region.ndim == 2: + bg_region_ = np.mean(bg_region, axis=1) + + else: + bg_region_ = bg_region + + return im_stack - bg_region_[:, np.newaxis, np.newaxis] + + +def classify(img_stack, correlation="Pearson"): + img_stack_ = img_stack + a, b, c = np.shape(img_stack_) + norm_img_stack = normalize(img_stack_) + f = np.reshape(norm_img_stack, (a, (b * c))) + + max_x, max_y = np.where(norm_img_stack.sum(0) == (norm_img_stack.sum(0)).max()) + ref = norm_img_stack[:, int(max_x), int(max_y)] + corr = np.zeros(len(f.T)) + for s in range(len(f.T)): + if correlation == "Kendall": + r, p = stats.kendalltau(ref, f.T[s]) + elif correlation == "Pearson": + r, p = stats.pearsonr(ref, f.T[s]) + + corr[s] = r + + cluster_image = np.reshape(corr, (b, c)) + return (cluster_image ** 3), img_stack_ + + +def correlation_kmeans(img_stack, n_clusters, correlation="Pearson"): + img, bg_image = classify(img_stack, correlation) + img[np.isnan(img)] = -99999 + X = img.reshape((-1, 1)) + k_means = sc.KMeans(n_clusters) + k_means.fit(X) + + X_cluster = k_means.labels_ + X_cluster = X_cluster.reshape(img.shape) + 1 + + return X_cluster + + +def cluster_stack( + im_array, method="KMeans", n_clusters_=4, decomposed=False, decompose_method="PCA", decompose_comp=2 +): + a, b, c = im_array.shape + + if method == "Correlation-Kmeans": + + X_cluster = correlation_kmeans(im_array, n_clusters_, correlation="Pearson") + + else: + + methods = { + "MiniBatchKMeans": sc.MiniBatchKMeans, + "KMeans": sc.KMeans, + "MeanShift": sc.MeanShift, + "Spectral Clustering": sc.SpectralClustering, + "Affinity Propagation": sc.AffinityPropagation, + } + + if decomposed: + im_array = denoise_with_decomposition(im_array, method_=decompose_method, n_components=decompose_comp) + + flat_array = np.reshape(im_array, (a, (b * c))) + init_cluster = methods[method](n_clusters=n_clusters_) + init_cluster.fit(np.transpose(flat_array)) + X_cluster = init_cluster.labels_.reshape(b, c) + 1 + + decon_spectra = np.zeros((a, n_clusters_)) + decon_images = np.zeros((n_clusters_, b, c)) + + for i in range(n_clusters_): + mask_i = np.where(X_cluster == (i + 1), X_cluster, 0) + spec_i = get_sum_spectra(im_array * mask_i) + decon_spectra[:, i] = spec_i + decon_images[i] = im_array.sum(0) * mask_i + + return decon_images, X_cluster, decon_spectra + +def kmeans_variance(im_array): + a, b, c = im_array.shape + flat_array = np.reshape(im_array, (a, (b * c))) + var = np.arange(24) + clust_n = np.arange(24) + 2 + + for clust in var: + init_cluster = sc.KMeans(n_clusters=int(clust + 2)) + init_cluster.fit(np.transpose(flat_array)) + var_ = init_cluster.inertia_ + var[clust] = np.float64(var_) + + return clust_n, var + + +def pca_scree(im_stack): + new_image = im_stack.transpose(2, 1, 0) + x, y, z = np.shape(new_image) + img_ = np.reshape(new_image, (x * y, z)) + # pca = sd.PCA(z) + # pca.fit(img_) + pca = sd.TruncatedSVD(z - 1) + pca.fit(img_) + var = pca.singular_values_ + # var = pca.singular_values_ + return var + + +def decompose_stack(im_stack, decompose_method="PCA", n_components_=3): + new_image = im_stack.transpose(2, 1, 0) + new_image[new_image<0] = 0 + + x, y, z = np.shape(new_image) + img_ = np.reshape(new_image, (x * y, z)) + methods_dict = { + "PCA": sd.PCA, + "IncrementalPCA": sd.IncrementalPCA, + "NMF": sd.NMF, + "FastICA": sd.FastICA, + "DictionaryLearning": sd.MiniBatchDictionaryLearning, + "FactorAnalysis": sd.FactorAnalysis, + "TruncatedSVD": sd.TruncatedSVD, + } + + _mdl = methods_dict[decompose_method](n_components=n_components_) + + ims = (_mdl.fit_transform(img_).reshape(x, y, n_components_)).transpose(2, 1, 0) + spcs = _mdl.components_.transpose() + decon_spetra = np.zeros((z, n_components_)) + decom_map = np.zeros((ims.shape)) + + for i in range(n_components_): + f = ims.copy()[i] + f[f < 0] = 0 + f = np.where(f > 3 * np.std(f), f, 0) + spec_i = ((new_image.T * f).sum(1)).sum(1) + decon_spetra[:, i] = spec_i + + f[f > 0] = i + 1 + decom_map[i] = f + decom_map = decom_map.sum(0) + + return np.float32(ims), spcs, decon_spetra, decom_map + + +def denoise_with_decomposition(img_stack, method_="PCA", n_components=4): + new_image = img_stack.transpose(2, 1, 0) + x, y, z = np.shape(new_image) + img_ = np.reshape(new_image, (x * y, z)) + + methods_dict = { + "PCA": sd.PCA, + "IncrementalPCA": sd.IncrementalPCA, + "NMF": sd.NMF, + "FastICA": sd.FastICA, + "DictionaryLearning": sd.DictionaryLearning, + "FactorAnalysis": sd.FactorAnalysis, + "TruncatedSVD": sd.TruncatedSVD, + } + + decomposed = methods_dict[method_](n_components=n_components) + + ims = (decomposed.fit_transform(img_).reshape(x, y, n_components)).transpose(2, 1, 0) + ims[ims < 0] = 0 + ims[ims > 0] = 1 + mask = ims.sum(0) + mask[mask > 1] = 1 + # mask = uniform_filter(mask) + filtered = img_stack * mask + # plt.figure() + # plt.imshow(filtered.sum(0)) + # plt.title('background removed') + # plt.show() + return remove_nan_inf(filtered) + + +def interploate_E(refs, e): + n = np.shape(refs)[1] + refs = np.array(refs) + ref_e = refs[:, 0] + ref = refs[:, 1:n] + all_ref = [] + for i in range(n - 1): + ref_i = np.interp(e, ref_e, ref[:, i]) + all_ref.append(ref_i) + return np.array(all_ref) + + +def getStats(spec, fit, num_refs=2): + stats = {} + + r_factor = (np.sum(spec - fit) ** 2) / np.sum(spec ** 2) + stats["R_Factor"] = np.around(r_factor, 5) + + y_mean = np.sum(spec) / len(spec) + SS_tot = np.sum((spec - y_mean) ** 2) + SS_res = np.sum((spec - fit) ** 2) + r_square = 1 - (SS_res / SS_tot) + stats["R_Square"] = np.around(r_square, 4) + + # https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.chisquare.html + # https://en.wikipedia.org/wiki/Chi-squared_distribution + # https://ned.ipac.caltech.edu/level5/Leo/Stats2_4.html + chisq = np.sum(((spec - fit) ** 2) / np.var(spec)) + # chisq = np.sum((spec - fit) ** 2) + stats["Chi_Square"] = np.around(chisq, 5) + + red_chisq = chisq / (len(spec) - num_refs) + stats["Reduced Chi_Square"] = np.around(red_chisq, 5) + + return stats + + +def xanes_fitting_1D(spec, e_list, refs, method="NNLS", alphaForLM=0.01): + """Linear combination fit of image data with reference standards""" + + int_refs = interploate_E(refs, e_list) + + if method == "NNLS": + coeffs, r = opt.nnls(int_refs.T, spec) + + elif method == "LASSO": + lasso = linear_model.Lasso(positive=True, alpha=alphaForLM) # lowering alpha helps with 1D fits + fit_results = lasso.fit(int_refs.T, spec) + coeffs = fit_results.coef_ + + elif method == "RIDGE": + ridge = linear_model.Ridge(alpha=alphaForLM) + fit_results = ridge.fit(int_refs.T, spec) + coeffs = fit_results.coef_ + + fit = coeffs @ int_refs + stats = getStats(spec, fit, num_refs=np.min(np.shape(int_refs.T))) + + return stats, np.around(coeffs, 4) + + +def xanes_fitting(im_stack, e_list, refs, method="NNLS", alphaForLM=0.1, binStack=False): + """Linear combination fit of image data with reference standards""" + + if binStack: + im_stack = resize_stack(im_stack, scaling_factor=4) + + en, im1, im2 = np.shape(im_stack) + im_array = im_stack.reshape(en, im1 * im2) + coeffs_arr = [] + r_factor_arr = [] + lasso = linear_model.Lasso(positive=True, alpha=alphaForLM) + for n, i in enumerate(range(im1 * im2)): + stats, coeffs = xanes_fitting_1D(im_array[:, i], e_list, refs, method=method, alphaForLM=alphaForLM) + coeffs_arr.append(coeffs) + r_factor_arr.append(stats["R_Factor"]) + + abundance_map = np.reshape(coeffs_arr, (im1, im2, -1)) + r_factor_im = np.reshape(r_factor_arr, (im1, im2)) + + return abundance_map, r_factor_im, np.mean(coeffs_arr, axis=0) + + +def xanes_fitting_Line(im_stack, e_list, refs, method="NNLS", alphaForLM=0.05): + """Linear combination fit of image data with reference standards""" + en, im1, im2 = np.shape(im_stack) + im_array = np.mean(im_stack, 2) + coeffs_arr = [] + meanStats = {"R_Factor": 0, "R_Square": 0, "Chi_Square": 0, "Reduced Chi_Square": 0} + + for i in range(im1): + stats, coeffs = xanes_fitting_1D(im_array[:, i], e_list, refs, method=method, alphaForLM=alphaForLM) + coeffs_arr.append(coeffs) + for key in stats.keys(): + meanStats[key] += stats[key] + + for key, vals in meanStats.items(): + meanStats[key] = np.around((vals / im1), 5) + + return meanStats, np.mean(coeffs_arr, axis=0) + + +def xanes_fitting_Binned(im_stack, e_list, refs, method="NNLS", alphaForLM=0.05): + """Linear combination fit of image data with reference standards""" + + im_stack = resize_stack(im_stack, scaling_factor=10) + # use a simple filter to find threshold value + val = filters.threshold_otsu(im_stack[-1]) + en, im1, im2 = np.shape(im_stack) + im_array = im_stack.reshape(en, im1 * im2) + coeffs_arr = [] + meanStats = {"R_Factor": 0, "R_Square": 0, "Chi_Square": 0, "Reduced Chi_Square": 0} + + specs_fitted = 0 + total_spec = im1 * im2 + for i in range(total_spec): + spec = im_array[:, i] + # do not fit low intensity/background regions + if spec[-1] > val: + specs_fitted += 1 + stats, coeffs = xanes_fitting_1D(spec / spec[-1], e_list, refs, method=method, alphaForLM=alphaForLM) + coeffs_arr.append(coeffs) + for key in stats.keys(): + meanStats[key] += stats[key] + else: + pass + + for key, vals in meanStats.items(): + meanStats[key] = np.around((vals / specs_fitted), 6) + # print(f"{specs_fitted}/{total_spec}") + return meanStats, np.mean(coeffs_arr, axis=0) + + +def create_df_from_nor(athenafile="fe_refs.nor"): + """create pandas dataframe from athena nor file, first column + is energy and headers are sample names""" + + refs = np.loadtxt(athenafile) + n_refs = refs.shape[-1] + skip_raw_n = n_refs + 6 + + df = pd.read_table( + athenafile, delim_whitespace=True, skiprows=skip_raw_n, header=None, usecols=np.arange(0, n_refs) + ) + df2 = pd.read_table( + athenafile, delim_whitespace=True, skiprows=skip_raw_n - 1, usecols=np.arange(0, n_refs + 1) + ) + new_col = df2.columns.drop("#") + df.columns = new_col + return df, list(new_col) + + +def create_df_from_nor_try2(athenafile="fe_refs.nor"): + """create pandas dataframe from athena nor file, first column + is energy and headers are sample names""" + + refs = np.loadtxt(athenafile) + n_refs = refs.shape[-1] + df_refs = pd.DataFrame(refs) + + df = pd.read_csv(athenafile, header=None) + new_col = list((str(df.iloc[n_refs + 5].values)).split(" ")[2::2]) + df_refs.columns = new_col + + return df_refs, list(new_col) + + +def energy_from_logfile(logfile="maps_log_tiff.txt"): + df = pd.read_csv(logfile, header=None, delim_whitespace=True, skiprows=9) + return df[9][df[7] == "energy"].values.astype(float) + + +def xanesNormalization(e, mu, e0=7125, step=None, + nnorm=2, nvict=0, pre1=None, pre2=-50, + norm1=100, norm2=None, method="pre_edge", + useFlattened=False, Elemline = "Fe_K"): + + elem, line = Elemline.split('_') + elemZ = xraydb.atomic_number(elem) + dat = Group(name='larchgroup', col1=e, col2=mu) + + + if method == "guess": + result = preedge(e, mu, e0, step=step, nnorm=nnorm, nvict=nvict) + + return result["pre1"], result["pre2"], result["norm1"], result["norm2"] + + elif method == "mback": + mback(e,mu, group=dat, z=elemZ, edge=line, e0=e0,fit_erfc=False) + return dat.f2, dat.fpp + + else: + pre_edge(e, mu,group=dat,e0=e0, step=step, nnorm=nnorm,nvict=nvict, pre1=pre1, + pre2=pre2, norm1=norm1,norm2=norm2, make_flat = True) + + if useFlattened: + normSpec = dat.flat + else: + normSpec = dat.norm + + return dat.pre_edge, dat.post_edge, normSpec + + +def xanesNormStack(e_list, im_stack, e0=7125, step=None, + nnorm=2, nvict=0, pre1=None, pre2=-50, + norm1=100, norm2=None, useFlattened=False, ignorePostEdgeNorm=False): + en, im1, im2 = np.shape(im_stack) + im_array = im_stack.reshape(en, im1 * im2) + normedStackArray = np.zeros_like(im_array) + dat = Group(name='larchgroup', col1=e_list, col2=get_mean_spectra(im_stack)) + + + for i in range(im1 * im2): + pre_edge(e_list, im_array[:, i], e0=e0, group=dat, step=step, nnorm=nnorm, + nvict=nvict, pre1=pre1, pre2=pre2, norm1=norm1, norm2=norm2,make_flat = True) + + if useFlattened: + normSpec = dat.flat + else: + normSpec = dat.norm + + if ignorePostEdgeNorm: + normedStackArray[:, i] = normSpec * dat.post_edge + else: + normedStackArray[:, i] = normSpec + + return remove_nan_inf(np.reshape(normedStackArray, (en, im1, im2))) + +def getDeconvolutedXANESSpectrum(xanesStack, chemMapStack, energy, clusterSigma=1): + compXanesSpetraAll = pd.DataFrame() + compXanesSpetraAll['Energy'] = energy + + for n, compImage in enumerate(chemMapStack): + mask = np.where(compImage > clusterSigma * np.std(compImage), compImage, 0) + compXanesSpetraAll[f'Component_{n + 1}'] = get_mean_spectra(xanesStack * mask) + return compXanesSpetraAll + + +def align_stack( + stack_img, ref_image_void=True, ref_stack=None, transformation=StackReg.TRANSLATION, reference="previous" +): + + """Image registration flow using pystack reg""" + + # all the options are in one function + + sr = StackReg(transformation) + + if ref_image_void: + tmats_ = sr.register_stack(stack_img, reference=reference) + + else: + tmats_ = sr.register_stack(ref_stack, reference=reference) + out_ref = sr.transform_stack(ref_stack) + + out_stk = sr.transform_stack(stack_img, tmats=tmats_) + return np.float32(out_stk), tmats_ + + +def align_simple(stack_img, transformation=StackReg.TRANSLATION, reference="previous"): + + sr = StackReg(transformation) + tmats_ = sr.register_stack(stack_img, reference="previous") + for i in range(10): + out_stk = sr.transform_stack(stack_img, tmats=tmats_) + return np.float32(out_stk) + + +def align_with_tmat(stack_img, tmat_file, transformation=StackReg.TRANSLATION): + + sr = StackReg(transformation) + out_stk = sr.transform_stack(stack_img, tmats=tmat_file) + return np.float32(out_stk) + + +def align_stack_iter( + stack, + ref_stack_void=True, + ref_stack=None, + transformation=StackReg.TRANSLATION, + method=("previous", "first"), + max_iter=2, +): + if ref_stack_void: + ref_stack = stack + + for i in range(max_iter): + sr = StackReg(transformation) + for ii in range(len(method)): + print(ii, method[ii]) + tmats = sr.register_stack(ref_stack, reference=method[ii]) + ref_stack = sr.transform_stack(ref_stack) + stack = sr.transform_stack(stack, tmats=tmats) + + return np.float32(stack) + + +def applyMaskGetMeanSpectrum(im_stack, mask): + """A 2d mask to multiply with the 3d xanes stack and returns mean spectrum""" + + masked_stack = im_stack * mask + return get_mean_spectra(masked_stack) + + +def modifyStack( + raw_stack, + normalizeStack=False, + normToPoint=-1, + applySmooth=False, + smoothWindowSize=3, + applyThreshold=False, + thresholdValue=0, + removeOutliers=False, + nSigmaOutlier=3, + applyTranspose=False, + transposeVals=(0, 1, 2), + applyCrop=False, + cropVals=(0, 1, 2), + removeEdges=False, + resizeStack=False, + upScaling=False, + binFactor=2, +): + + """A giant function to modify the stack with many possible operations. + all the changes can be saved to a jason file as a config file. Enabling and + distabling the sliders is a problem""" + + """ + normStack = normalize(raw_stack, norm_point=normToPoint) + smoothStack = smoothen(raw_stack, w_size= smoothWindowSize) + thresholdStack = clean_stack(raw_stack, auto_bg=False, bg_percentage = thresholdValue) + outlierStack = remove_hot_pixels(raw_stack, NSigma=nSigmaOutlier) + transposeStack = np.transpose(raw_stack, transposeVals) + croppedStack = raw_stack[cropVals] + edgeStack = remove_edges(raw_stack) + binnedStack = resize_stack(raw_stack,upscaling=upScaling,scaling_factor=binFactor) + + """ + + if removeOutliers: + modStack = remove_hot_pixels(raw_stack, NSigma=nSigmaOutlier) + + else: + modStack = raw_stack + + if applyThreshold: + modStack = clean_stack(modStack, auto_bg=False, bg_percentage=thresholdValue) + + else: + pass + + if applySmooth: + modStack = smoothen(modStack, w_size=smoothWindowSize) + + else: + pass + + if applyTranspose: + modStack = np.transpose(modStack, transposeVals) + + else: + pass + + if applyCrop: + modStack = modStack[cropVals] + + else: + pass + + if normalizeStack: + modStack = normalize(raw_stack, norm_point=normToPoint) + else: + pass