Skip to content

Commit

Permalink
Adding Pixelcalibration Widget
Browse files Browse the repository at this point in the history
  • Loading branch information
beniroquai committed Nov 5, 2022
1 parent 80e5fdf commit a9a9571
Show file tree
Hide file tree
Showing 11 changed files with 308 additions and 10 deletions.
3 changes: 2 additions & 1 deletion imswitch/imcontrol/controller/MasterController.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from imswitch.imcommon.model import VFileItem
from imswitch.imcontrol.model import (
DetectorsManager, LasersManager, MultiManager, PositionersManager,
RecordingManager, RS232sManager, ScanManager, SLMManager, SIMManager, LEDMatrixsManager, MCTManager, ISMManager, UC2ConfigManager, AutofocusManager, HistoScanManager
RecordingManager, RS232sManager, ScanManager, SLMManager, SIMManager, LEDMatrixsManager, MCTManager, ISMManager, UC2ConfigManager, AutofocusManager, HistoScanManager, PixelCalibrationManager
)


Expand Down Expand Up @@ -40,6 +40,7 @@ def __init__(self, setupInfo, commChannel, moduleCommChannel):
self.simManager = SIMManager(self.__setupInfo.sim)
self.mctManager = MCTManager(self.__setupInfo.mct)
self.HistoScanManager = HistoScanManager(self.__setupInfo.HistoScan)
self.PixelCalibrationManager = PixelCalibrationManager(self.__setupInfo.PixelCalibration)
self.AutoFocusManager = AutofocusManager(self.__setupInfo.autofocus)
self.ismManager = ISMManager(self.__setupInfo.ism)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import json
import os

import numpy as np
import time
import tifffile as tif
import threading
from datetime import datetime
import cv2

from imswitch.imcommon.model import dirtools, initLogger, APIExport
from ..basecontrollers import ImConWidgetController
from imswitch.imcommon.framework import Signal, Thread, Worker, Mutex, Timer
import time

from ..basecontrollers import LiveUpdatedController

#import NanoImagingPack as nip

class PixelCalibrationController(LiveUpdatedController):
"""Linked to PixelCalibrationWidget."""

sigImageReceived = Signal()

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._logger = initLogger(self)

# Connect PixelCalibrationWidget signals
#self._widget.PixelCalibrationLabelInfo.clicked.connect()
self._widget.PixelCalibrationSnapPreviewButton.clicked.connect(self.snapPreview)
self._widget.PixelCalibrationUndoButton.clicked.connect(self.undoSelection)
self._widget.PixelCalibrationCalibrateButton.clicked.connect(self.startPixelCalibration)
self.pixelSize=0
# select detectors
allDetectorNames = self._master.detectorsManager.getAllDeviceNames()
self.detector = self._master.detectorsManager[allDetectorNames[0]]


def undoSelection(self):
# recover the previous selection
self._widget.canvas.undoSelection()

def snapPreview(self):
self._logger.info("Snap preview...")
self.previewImage = self.detector.getLatestFrame()
self._widget.canvas.setImage(self.previewImage)

def startPixelCalibration(self):
# initilaze setup
# this is not a thread!
self.pixelSize = self._widget.getPixelSize()
self._widget.setInformationLabel(str(self.pixelSize)+" µm")




# Copyright (C) 2020-2021 ImSwitch developers
# This file is part of ImSwitch.
#
# ImSwitch is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# ImSwitch is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
1 change: 1 addition & 0 deletions imswitch/imcontrol/controller/controllers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from .SIMController import SIMController
from .MCTController import MCTController
from .HistoScanController import HistoScanController
from .PixelCalibrationController import PixelCalibrationController
from .ISMController import ISMController
from .ScanController import ScanController
from .SettingsController import SettingsController
Expand Down
7 changes: 7 additions & 0 deletions imswitch/imcontrol/model/SetupInfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,10 @@ class MCTInfo:
class HistoScanInfo:
pass

@dataclass(frozen=True)
class PixelCalibrationInfo:
pass

@dataclass(frozen=True)
class ISMInfo:
wavelength: int
Expand Down Expand Up @@ -311,6 +315,9 @@ class SetupInfo:
HistoScan: Optional[HistoScanInfo] = field(default_factory=lambda: None)
""" HistoScan settings. Required to be defined to use HistoScan functionality. """

PixelCalibration: Optional[PixelCalibrationInfo] = field(default_factory=lambda: None)
""" PixelCalibration settings. Required to be defined to use PixelCalibration functionality. """

uc2Config: Optional[UC2ConfigInfo] = field(default_factory=lambda: None)
""" MCT settings. Required to be defined to use MCT functionality. """

Expand Down
9 changes: 1 addition & 8 deletions imswitch/imcontrol/model/managers/HistoScanManager.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,10 @@ def __init__(self, HistoScanInfo, *args, **kwargs):

if HistoScanInfo is None:
return



self.update()

def update(self):
#self.allPatternsPaths
#self.maskDouble = self.__masks[0].concat(self.__masks[1])
#self.maskCombined = self.maskDouble
#self.sigHistoScanMaskUpdated.emit(self.maskCombined)

#returnmask = self.maskDouble
return None #returnmask.image()

# Copyright (C) 2020-2021 ImSwitch developers
Expand Down
43 changes: 43 additions & 0 deletions imswitch/imcontrol/model/managers/PixelCalibrationManager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import enum
import glob
import cv2
import os

import numpy as np
from PIL import Image
from scipy import signal as sg

from imswitch.imcommon.framework import Signal, SignalInterface
from imswitch.imcommon.model import initLogger


class PixelCalibrationManager(SignalInterface):
sigPixelCalibrationMaskUpdated = Signal(object) # (maskCombined)

def __init__(self, PixelCalibrationInfo, *args, **kwargs):
super().__init__(*args, **kwargs)
self.__logger = initLogger(self)

if PixelCalibrationInfo is None:
return

self.update()

def update(self):
return None #returnmask.image()

# Copyright (C) 2020-2021 ImSwitch developers
# This file is part of ImSwitch.
#
# ImSwitch is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# ImSwitch is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
1 change: 1 addition & 0 deletions imswitch/imcontrol/model/managers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@
from .SIMManager import SIMManager
from .MCTManager import MCTManager
from .HistoScanManager import HistoScanManager
from .PixelCalibrationManager import PixelCalibrationManager
from .ISMManager import ISMManager
from .ScanManager import ScanManager
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ def stopAcquisitionForROIChange(self):
def finalize(self) -> None:
super().finalize()
self.__logger.debug('Safely disconnecting the camera...')
self._camera.close()
#TODO: IMPLEMENT self._camera.close()

@property
def pixelSizeUm(self):
Expand Down
1 change: 1 addition & 0 deletions imswitch/imcontrol/view/ImConMainView.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ def __init__(self, options, viewSetupInfo, *args, **kwargs):
'SIM': _DockInfo(name='SIM', yPosition=0),
'MCT': _DockInfo(name='MCT', yPosition=0),
'HistoScan': _DockInfo(name='HistoScan', yPosition=1),
'PixelCalibration': _DockInfo(name='PixelCalibration', yPosition=1),
'ISM': _DockInfo(name='ISM', yPosition=0),
'Laser': _DockInfo(name='Laser Control', yPosition=0),
'EtSTED': _DockInfo(name='EtSTED', yPosition=0),
Expand Down
178 changes: 178 additions & 0 deletions imswitch/imcontrol/view/widgets/PixelCalibrationWidget.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
import numpy as np
import pyqtgraph as pg
import cv2
import copy
from qtpy import QtCore, QtWidgets, QtGui, QtWidgets
from PyQt5.QtGui import QPixmap, QImage

import sys
from PyQt5.QtWidgets import (QApplication, QLabel, QWidget)
from PyQt5.QtGui import QPainter, QColor, QPen
from PyQt5.QtCore import Qt
from qtpy import QtCore, QtWidgets, QtGui, QtWidgets
import cv2
import numpy as np
import copy
from PyQt5.QtGui import QPixmap, QImage




from imswitch.imcontrol.view import guitools
from .basewidgets import NapariHybridWidget



class PixelCalibrationWidget(NapariHybridWidget):
""" Widget containing PixelCalibration interface. """

def __post_init__(self):
#super().__init__(*args, **kwargs)
# initialize all GUI elements

# Add Painter for drawing a line on pixels
self.canvas = Canvas()

# Snap Preview Button
self.PixelCalibrationSnapPreviewButton = guitools.BetterPushButton('Snap Preview')
self.PixelCalibrationSnapPreviewButton.setCheckable(False)

# Undo Button
self.PixelCalibrationUndoButton = guitools.BetterPushButton('Undo')
self.PixelCalibrationUndoButton.setCheckable(False)

# editable for entering pixelvalues
self.PixelCalibrationLabelKnownDistance = QtWidgets.QLabel('Known Distance: (µm)')
self.PixelCalibrationEditFileName = QtWidgets.QLineEdit('100')
self.PixelCalibrationCalibrateButton = guitools.BetterPushButton('Start')
self.PixelCalibrationCalibrateButton.setCheckable(False)
self.PixelCalibrationLabelInfo = QtWidgets.QLabel("")

# Defining layout
self.grid = QtWidgets.QGridLayout()
self.setLayout(self.grid)

#
self.grid.addWidget(self.canvas, 0, 0, 3, 3)

#
self.grid.addWidget(self.PixelCalibrationSnapPreviewButton, 1, 0, 1, 1)
self.grid.addWidget(self.PixelCalibrationUndoButton, 1, 1, 1, 1)
self.grid.addWidget(self.PixelCalibrationCalibrateButton, 1, 2, 1, 1)

#
self.grid.addWidget(self.PixelCalibrationLabelKnownDistance, 2, 0, 1, 1)
self.grid.addWidget(self.PixelCalibrationEditFileName, 2, 1, 1, 1)
self.grid.addWidget(self.PixelCalibrationLabelInfo, 2, 2, 1, 1)

self.layer = None


def getPixelSize(self):
knownDistance = int(self.PixelCalibrationEditFileName.text())
lineLength = self.canvas.getLineLength()
pixelSize = knownDistance/lineLength
return pixelSize


def getCoordinateList(self):
return self.canvas.getCoordinateList()

def setInformationLabel(self, information):
self.PixelCalibrationLabelInfo.setText(information)

def setPreviewImage(self, image):
self.canvas.setImage(image)

class Canvas(QtWidgets.QLabel):
def __init__(self):
super().__init__()
self.dimensions = (400,400)
self.setFixedSize(self.dimensions[0],self.dimensions[1])
self.setImage()

self.pos = None
#self.setMouseTracking(False)
self.pressPos = (0,0)
self.lineCoordinates = (0,0,0,0)

self.isTracking = False


def setImage(self, npImage=None, pathImage='histo.jpg'):

if npImage is None:
npImage = np.array(cv2.imread(pathImage))
npImage = cv2.resize(npImage, self.dimensions)
if len(npImage.shape) == 2:
npImage = np.repeat(npImage[:,:,np.newaxis], 3, axis=2)

height, width, channel = npImage.shape
bytesPerLine = 3 * width
qImage = QImage(npImage.data, width, height, bytesPerLine, QImage.Format_RGB888)
self.imageNow = QPixmap(qImage)

self.setPixmap(self.imageNow.scaled(self.dimensions[0], self.dimensions[1], QtCore.Qt.KeepAspectRatio))

def enterEvent(self, event):
super().enterEvent(event)
self.isTracking = True

def leaveEvent(self, event):
super().leaveEvent(event)
self.isTracking = False

def mouseMoveEvent(self, event):
self.pos = event.pos()
self.update()

def paintEvent(self, event):
if self.pos and self.isTracking:
painter = QPainter(self)
pen = QtGui.QPen()
pen.setWidth(4)
pen.setColor(QtGui.QColor('yellow'))
painter.setPen(pen)
try:
painter.drawLine(self.pos.x(), self.pos.y(), self.pressPos.x(), self.pressPos.y())
painter.end()
self.lineCoordinates = [self.pos.x(), self.pos.y(), self.pressPos.x(), self.pressPos.y()]
except Exception as e:
print (e)

def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.pressPos = event.pos()
self.setMouseTracking(True)

def mouseReleaseEvent(self, event):
# ensure that the left button was pressed *and* released within the
# geometry of the widget; if so, emit the signal;
if (self.pressPos is not None and
event.button() == Qt.LeftButton and
event.pos() in self.rect()):
self.setMouseTracking(False)
self.pressPos = None

def getLineLength(self):
try:
lineLength = np.sqrt(np.abs(self.lineCoordinates[2]-self.lineCoordinates[0])**2+np.abs(self.lineCoordinates[3]-self.lineCoordinates[1])**2)
except:
lineLength = 1
return lineLength

# Copyright (C) 2020-2021 ImSwitch developers
# This file is part of ImSwitch.
#
# ImSwitch is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# ImSwitch is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
1 change: 1 addition & 0 deletions imswitch/imcontrol/view/widgets/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from .SIMWidget import SIMWidget
from .MCTWidget import MCTWidget
from .HistoScanWidget import HistoScanWidget
from .PixelCalibrationWidget import PixelCalibrationWidget
from .SquidStageScanWidget import SquidStageScanWidget
from .ISMWidget import ISMWidget
from .ScanWidget import ScanWidget
Expand Down

0 comments on commit a9a9571

Please sign in to comment.