Commit 819efb02 authored by Robert's avatar Robert Committed by Robert Ohmacht

-initial integration of ScenePyramid from GepardEvaluation into Gepard

parent 1fff5fef
......@@ -136,6 +136,9 @@ class DataSet(object):
self.signx = 1.
self.signy = -1.
# tiling parameters
self.pyramidParams = None
# parameters specifically for raman scan
self.pshift = None # shift of raman scan position relative to image center
self.coordOffset = [0, 0] #offset of entire coordinate system
......@@ -170,6 +173,12 @@ class DataSet(object):
def __eq__(self, other):
return recursiveDictCompare(self.__dict__, other.__dict__)
def getPyramidParams(self):
return self.pyramidParams
def setPyramidParams(self, pyramid_params):
self.pyramidParams = pyramid_params
def getPixelScale(self, mode=None):
if mode is None:
mode = self.imagescanMode
......@@ -275,6 +284,12 @@ class DataSet(object):
self.__dict__.update(loadData(fname).__dict__)
return fname
def getTilePath(self):
scandir = os.path.join(self.path, "tiles")
if not os.path.exists(scandir):
os.mkdir(scandir)
return scandir
def getScanPath(self):
scandir = os.path.join(self.path, "scanimages")
if not os.path.exists(scandir):
......
......@@ -27,6 +27,7 @@ from threading import Thread
from .segmentation import Segmentation
from .analysis.particleCharacterization import getParticleStatsWithPixelScale, loadZValImageFromDataset
from .errors import InvalidParticleError
from .scenePyramid import ScenePyramid
Nscreen = 1000
......@@ -269,14 +270,15 @@ class ParticleDetectionView(QtWidgets.QWidget):
imageUpdate = QtCore.pyqtSignal(str, name='imageUpdate') #str = 'df' (= darkfield) or 'bf' (=bright field)
detectionFinished = QtCore.pyqtSignal(name='detectionFinished')
def __init__(self, img, dataset, parent=None):
def __init__(self, pyramid: ScenePyramid, dataset, parent=None):
super().__init__(parent, QtCore.Qt.Window)
self.dataset = self.verifySeedpoints(dataset)
self.img = img
self.imgclip = 0,0,0,0
self.pyramid = pyramid
self.img = pyramid.getFullImage(.5)
self.imgclip = 0, 0, 0, 0
self.seg = Segmentation(self.dataset, self)
self.thread = None
self.view = parent
self.view : QtWidgets.QGraphicsView = parent
vbox = QtWidgets.QVBoxLayout()
hbox = QtWidgets.QHBoxLayout()
......@@ -529,33 +531,69 @@ class ParticleDetectionView(QtWidgets.QWidget):
self.updateImageSeeds()
def setImageCenter(self, center=None):
'''
as more than the tiles are part of the scene
bounding rect may be bigger than just a rect around the tiles
tiles should be grouped
'''
width, height = self.pyramid.getBoundingRectDim()
if center is None:
centerx = self.img.shape[1]//2
centery = self.img.shape[0]//2
centerx = width//2
centery = height//2
else:
centerx, centery = center
if self.img.shape[0]<Nscreen:
centery = self.img.shape[0]//2
if self.img.shape[1]<Nscreen:
centerx = self.img.shape[1]//2
if height < Nscreen:
centery = height//2
if width < Nscreen:
centerx = width//2
if not self.drag:
self.lastcenter = centerx, centery
n1, n2 = int(centery-Nscreen//2),int(centery+Nscreen//2+1)
m1, m2 = int(centerx-Nscreen//2),int(centerx+Nscreen//2+1)
if n1<0:
n2 -= n1
n1 = 0
if n2>self.img.shape[0]:
n1 -= n2-self.img.shape[0]
if n1<0: n1 = 0
n2 = self.img.shape[0]
if m1<0:
m2 -= m1
m1 = 0
if m2>self.img.shape[1]:
m1 -= m2-self.img.shape[1]
if m1<0: m1 = 0
m2 = self.img.shape[1]
y1, y2 = centery-Nscreen//2, centery+Nscreen//2+1
x1, x2 = centerx-Nscreen//2, centerx+Nscreen//2+1
if y1 < 0:
y2 -= y1
y1 = 0
if y2 > height:
y1 -= y2-height
if y1 < 0:
y1 = 0
y2 = height
if x1 < 0:
x2 -= x1
x1 = 0
if x2 > width:
x1 -= x2-width
if x1 < 0:
x1 = 0
x2 = width
self.imgclip = int(y1), int(y2), int(x1), int(x2)
self.subimg, pix = self.pyramid.getSubImage(self.imgclip)
'''
img = QtGui.QImage(x2 - x1, y2 - y1, QtGui.QImage.Format_RGB888)
self.view.scene().render(
QtGui.QPainter(img),
QtCore.QRectF(0, 0, img.width(), img.height()),
#QtCore.QRectF(x1 + x0, y1 + y0, x2 - x1, y2 - y1)
QtCore.QRectF(x1, y1, x2 - x1, y2 - y1)
)
#pix = self.view.grab(QtCore.QRect(x1 + x0, y1 + y0, x2 - x1, y2 - y1))
# @see https://stackoverflow.com/a/11399959
#img = pix.toImage().convertToFormat(QtGui.QImage.Format_RGB888)
ptr = img.bits()
#ptr.setsize(img.byteCount())
ptr.setsize(img.height() * img.width() * 3)
self.subimg = np.asarray(ptr).reshape(img.height(), img.width(), 3)
pix = QtGui.QPixmap()
pix.convertFromImage(img)
'''
'''
sub = self.img[n1:n2,m1:m2,:].copy()
self.imgclip = n1,n2,m1,m2
self.subimg = sub
......@@ -564,6 +602,7 @@ class ParticleDetectionView(QtWidgets.QWidget):
pix = QtGui.QPixmap()
pix.convertFromImage(QtGui.QImage(sub.data, width, height,
bytesPerLine, QtGui.QImage.Format_RGB888))
'''
self.imglabel.clearData()
self.imglabel.setPixmap(pix)
self.updateImageSeeds()
......
......@@ -27,8 +27,9 @@ from .helperfunctions import cv2imread_fix, cv2imwrite_fix
from .analysis.particleContainer import ParticleContainer
from .analysis import particleCharacterization as pc
from .errors import InvalidParticleError
from .scenePyramid import ScenePyramid
currentVersion = 4
currentVersion = 5
def legacyConversion(dset, recreatefullimage=False):
if dset.version==0:
......@@ -111,6 +112,12 @@ def legacyConversion(dset, recreatefullimage=False):
dset.version = 4
dset.save()
if dset.version == 4:
print("Converting legacy version 4 to 5")
ScenePyramid.createFromFullImage(dset)
dset.version = 5
dset.save()
# add later conversion for higher version numbers here
......
......@@ -106,7 +106,7 @@ class BackGroundManager(QtWidgets.QWidget):
p0 = [points[:,0].min(), points[:,1].max()]
p1 = [points[:,0].max(), points[:,1].min()]
reply = QtWidgets.QMessageBox.question(self, 'Message',f"The stage will move {round(3*width)} in x and {round(3*height)} in y.\nContinue?",
reply = QtWidgets.QMessageBox.question(self, 'Message', f"The stage will move {round(3*width)} in x and {round(3*height)} in y.\nContinue?",
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, QtWidgets.QMessageBox.No)
if reply == QtWidgets.QMessageBox.Yes:
fullimg = None
......
......@@ -31,6 +31,7 @@ from time import time
import datetime
from .opticalbackground import BackGroundManager
from .zlevelsetter import ZLevelSetter
from .scenePyramid import ScenePyramid
def scan(path, sol, zpositions, grid, controlclass, dataqueue,
stopevent, logpath='', ishdr=False):
......@@ -84,10 +85,10 @@ def scan(path, sol, zpositions, grid, controlclass, dataqueue,
def subtractBackground(image, background):
avg = np.mean(background)
subtracted = np.clip(np.array(image - background + avg, dtype = np.uint8), 0, 255)
subtracted = np.clip(np.array(image - background + avg, dtype=np.uint8), 0, 255)
return subtracted
def loadAndPasteImage(srcnames, fullimage, fullzval, width, height,
def loadAndPasteImage(srcnames, pyramid, fullzval, width, height,
rotationvalue, p0, p1, p, background=None):
colimgs = []
for name in srcnames:
......@@ -102,15 +103,22 @@ def loadAndPasteImage(srcnames, fullimage, fullzval, width, height,
c, s = np.cos(np.radians(rotationvalue)), np.sin(np.radians(rotationvalue))
dx, dy = (x-p0[0])/width*img.shape[1], (p0[1]-y)/height*img.shape[0]
M = np.float32([[c,s,dx],[-s,c,dy]])
if fullimage is not None:
cv2.warpAffine(img, M, (Nx, Ny), fullimage, borderMode=cv2.BORDER_TRANSPARENT)
M = np.float32([[c, s, dx], [-s, c, dy]])
dst = None
if pyramid is not None:
if fullzval is not None:
cv2.warpAffine(zval, M, (Nx, Ny), fullzval, borderMode=cv2.BORDER_TRANSPARENT)
dst = fullimage
zval = fullzval
else:
dst = cv2.warpAffine(img, M, (Nx, Ny))
zval = cv2.warpAffine(zval, M, (Nx, Ny))
pyramid.addSrcTile(
img,
np.float32([[c, s, 0], [-s, c, 0]]),
(dx, dy),
(Nx, Ny)
)
return dst, zval
......@@ -231,12 +239,13 @@ class OpticalScan(QtWidgets.QWidget):
def __init__(self, ramanctrl, dataset, logpath='', parent=None):
super().__init__(parent, QtCore.Qt.Window)
self.logpath = logpath
self.view = parent
self.view: QtWidgets.QGraphicsView = parent
mainLayout = QtWidgets.QVBoxLayout()
pointgroup = QtWidgets.QGroupBox("Point coordinates [µm]", self)
self.ramanctrl = ramanctrl
self.dataset = dataset
self.pyramid: ScenePyramid = None
self.positions = []
self.process = None
self.points = PointCoordinates(5, self.ramanctrl, self)
......@@ -425,6 +434,9 @@ class OpticalScan(QtWidgets.QWidget):
self.boundaryUpdate.emit()
self.prun.setEnabled(True)
def setPyramid(self, pyramid):
self.pyramid = pyramid
def resetDataset(self, ds):
self.dataset = ds
self.points.createWidgets(5, list(zip(ds.fitindices,ds.fitpoints)))
......@@ -482,22 +494,34 @@ class OpticalScan(QtWidgets.QWidget):
Nx, Ny = int((p1[0]-p0[0]+width)/width*img.shape[1]), int((p0[1]-p1[1]+height)/height*img.shape[0]) + 10 # + 10 because of rotation and hopefully it will be small
c, s = np.cos(np.radians(rotationvalue)), np.sin(np.radians(rotationvalue))
dx, dy = (x-p0[0])/width*img.shape[1], (p0[1]-y)/height*img.shape[0]
M = np.float32([[c,s,dx],[-s,c,dy]])
dst = cv2.warpAffine(img, M, (Nx, Ny))
if self.view.imgdata is not None and self.dataset.lastpos is not None:
lp = self.dataset.lastpos
dx, dy = (lp[0]-p0[0])/width*img.shape[1], (p0[1]-lp[1])/height*img.shape[0]
full = self.view.imgdata
M = np.float32([[1,0,dx],[0,1,dy]])
try:
full = cv2.warpAffine(full, M, (Nx, Ny)) #fails, if image dimensions are >32767x32767px...
dst = cv2.max(full, dst)
except:
QtWidgets.QMessageBox.critical(self, 'Error', 'Image is too large\nSelect smaller region.')
raise
return
self.view.imgdata = dst
M = np.float32([[c, s, dx], [-s, c, dy]])
#dst = cv2.warpAffine(img, M, (Nx, Ny))
M_rot = np.float32([[c, s, 0], [-s, c, 0]])
img_rot = cv2.warpAffine(img, M_rot, (img.shape[1], img.shape[0] + 10))
# calc new pixel start coords for tile
# @see https://stackoverflow.com/a/43166421/9880753
c_dest = cv2.transform(np.float32([[(0, 0)]]), np.float32([[c, s, dx], [-s, c, dy]]))
self.pyramid.addSrcTileSimple(
img_rot,
(c_dest[0][0][0], c_dest[0][0][1]),
p0
)
#if self.view.imgdata is not None and self.dataset.lastpos is not None:
# lp = self.dataset.lastpos
# dx, dy = (lp[0]-p0[0])/width*img.shape[1], (p0[1]-lp[1])/height*img.shape[0]
# full = self.view.imgdata
# M = np.float32([[1,0,dx],[0,1,dy]])
# try:
# full = cv2.warpAffine(full, M, (Nx, Ny)) #fails, if image dimensions are >32767x32767px...
# dst = cv2.max(full, dst)
# except:
# QtWidgets.QMessageBox.critical(self, 'Error', 'Image is too large\nSelect smaller region.')
# raise
# return
#self.view.imgdata = dst
self.dataset.lastpos = p0
self.dataset.maxdim = p0 + p1
self.dataset.readin = False
......@@ -621,7 +645,8 @@ class OpticalScan(QtWidgets.QWidget):
self.progressbar.setEnabled(True)
self.progressbar.setRange(0, len(self.dataset.grid))
self.progressbar.setValue(0)
self.view.imgdata = None
#self.view.imgdata = None
self.pyramid.resetScene()
self.view.blockUI()
grid = np.asarray(self.dataset.grid)
p0 = [grid[:,0].min(), grid[:,1].max()]
......@@ -657,7 +682,7 @@ class OpticalScan(QtWidgets.QWidget):
else:
background_img = None
self.view.imgdata, self.dataset.zvalimg = loadAndPasteImage(names, self.view.imgdata, self.dataset.zvalimg, width, height,
self.view.imgdata, self.dataset.zvalimg = loadAndPasteImage(names, self.pyramid, self.dataset.zvalimg, width, height,
rotationvalue, p0, p1, p, background=background_img)
self.progressbar.setValue(i+1)
if i>3:
......@@ -665,15 +690,19 @@ class OpticalScan(QtWidgets.QWidget):
ttot = timerunning*Ngrid/(i+1)
time2go = ttot - timerunning
self.progresstime.setText(self.timelabeltext + str(datetime.timedelta(seconds=round(time2go))))
self.imageUpdate.emit(self.view.microscopeMode)
# reload image in sampleview, calls loadPixmap
# not needed anymore as the scene gets manipulated directly via self.pyramid
#self.imageUpdate.emit(self.view.microscopeMode)
if i==Ngrid-1:
cv2imwrite_fix(self.dataset.getImageName(), cv2.cvtColor(self.view.imgdata, cv2.COLOR_RGB2BGR))
#cv2imwrite_fix(self.dataset.getImageName(), cv2.cvtColor(self.view.imgdata, cv2.COLOR_RGB2BGR))
self.dataset.saveZvalImg()
self.process.join()
self.dataqueue.close()
self.dataqueue.join_thread()
self.pyramid.toDataset()
if self.deleteImgChecker.isChecked():
path = self.dataset.getScanPath()
files = os.listdir(path)
......
......@@ -35,6 +35,7 @@ from .helperfunctions import polygoncovering, cv2imread_fix
from .analysis.colorlegend import getColorFromNameWithSeed
from .analysis.particleEditor import ParticleEditor
from .ramancom.configRaman import RamanConfigWin
from .scenePyramid import ScenePyramid
class SampleView(QtWidgets.QGraphicsView):
ScalingChanged = QtCore.pyqtSignal(float)
......@@ -44,13 +45,13 @@ class SampleView(QtWidgets.QGraphicsView):
super(SampleView, self).__init__()
self.logpath = logpath
self.item = QtWidgets.QGraphicsPixmapItem()
self.item.setPos(0, 0)
self.item.setAcceptedMouseButtons(QtCore.Qt.NoButton)
#self.item = QtWidgets.QGraphicsPixmapItem()
#self.item.setPos(0, 0)
#self.item.setAcceptedMouseButtons(QtCore.Qt.NoButton)
self.scaleFactor = 1.0
scene = QtWidgets.QGraphicsScene(self)
scene.setItemIndexMethod(QtWidgets.QGraphicsScene.NoIndex)
scene.addItem(self.item)
#scene.addItem(self.item)
scene.setBackgroundBrush(QtCore.Qt.darkGray)
self.setScene(scene)
self.setCacheMode(QtWidgets.QGraphicsView.CacheBackground)
......@@ -77,6 +78,7 @@ class SampleView(QtWidgets.QGraphicsView):
self.drag = None
self.mode = None
self.dataset = None
self.pyramid = ScenePyramid(self)
self.particleEditor = None
self.fititems = []
self.boundaryitems = [[],[]]
......@@ -159,6 +161,10 @@ class SampleView(QtWidgets.QGraphicsView):
if self.dataset is not None:
self.dataset.save()
def scrollContentsBy(self, dx: int, dy: int) -> None:
super().scrollContentsBy(dx, dy)
self.pyramid.onMove()
@QtCore.pyqtSlot()
def zoomIn(self):
self.zoomDisplay(1.25)
......@@ -179,7 +185,8 @@ class SampleView(QtWidgets.QGraphicsView):
Fits the window to show the entire sample.
:return:
"""
brect = self.item.sceneBoundingRect()
brect = self.scene().itemsBoundingRect()
#brect = self.item.sceneBoundingRect()
self.fitInView(0, 0, brect.width(), brect.height(), QtCore.Qt.KeepAspectRatio)
self.scaleFactor = self.transform().m11()
self.announceScaling()
......@@ -203,11 +210,17 @@ class SampleView(QtWidgets.QGraphicsView):
if mode == "OpticalScan":
self.oscanwidget.setVisible(True)
self.oscanwidget.resetDataset(self.dataset)
self.oscanwidget.setPyramid(self.pyramid)
elif mode == "ParticleDetection":
if self.detectionwidget is None:
print('creating new detect window')
self.detectionwidget = ParticleDetectionView(self.imgdata, self.dataset, self)
self.detectionwidget = ParticleDetectionView(
self.pyramid,
self.dataset,
self
)
#self.detectionwidget = ParticleDetectionView(self.imgdata, self.dataset, self)
self.detectionwidget.imageUpdate.connect(self.detectionUpdate)
self.detectionwidget.detectionFinished.connect(self.activateMaxMode)
self.updateSeedPointMarkers()
......@@ -254,6 +267,10 @@ class SampleView(QtWidgets.QGraphicsView):
self.dataset = loadData(fname)
self.setupParticleEditor()
self.setMicroscopeMode()
self.pyramid.fromDataset(self.dataset)
self.pyramid.setMicroscopeMode(self.microscopeMode)
self.imparent.setWindowTitle(self.dataset.name + (" SIMULATION" if simulatedRaman else ""))
self.imgdata = None
self.activateMaxMode(loadnew=True)
......@@ -273,6 +290,8 @@ class SampleView(QtWidgets.QGraphicsView):
self.dataset = DataSet(fname, newProject=True)
self.setupParticleEditor()
self.setMicroscopeMode()
self.pyramid.fromDataset(self.dataset)
self.pyramid.setMicroscopeMode(self.microscopeMode)
self.imparent.setWindowTitle(self.dataset.name + (" SIMULATION" if simulatedRaman else ""))
self.imgdata = None
self.activateMaxMode(loadnew=True)
......@@ -483,6 +502,7 @@ class SampleView(QtWidgets.QGraphicsView):
return
self.scaleFactor *= factor
self.scale(factor, factor)
self.pyramid.onScale()
self.announceScaling()
def announceScaling(self):
......@@ -520,6 +540,13 @@ class SampleView(QtWidgets.QGraphicsView):
self.prepareAnalysis()
self.update()
def addEmptyItem(self):
item = QtWidgets.QGraphicsPixmapItem()
item.setPos(0, 0)
item.setAcceptedMouseButtons(QtCore.Qt.NoButton)
item.setPixmap(QtGui.QPixmap())
self.scene().addItem(item)
@QtCore.pyqtSlot(str)
def loadPixmap(self, microscope_mode='df'):
"""
......@@ -529,38 +556,45 @@ class SampleView(QtWidgets.QGraphicsView):
"""
self.clearItems()
if self.dataset is None:
self.item.setPixmap(QtGui.QPixmap())
#self.item.setPixmap(QtGui.QPixmap())
None
else:
data = self.imgdata
fname = self.dataset.getImageName()
#data = self.imgdata
#fname = self.dataset.getImageName()
if self.mode == "ParticleDetection" or self.mode == "ParticleAnalysis":
self.resetParticleContours()
if data is None and os.path.exists(fname):
data = cv2imread_fix(fname)
self.imgdata = data
if data is not None:
height, width, channel = data.shape
bytesPerLine = 3 * width
pix = QtGui.QPixmap()
pix.convertFromImage(QtGui.QImage(data.data,
width, height, bytesPerLine, QtGui.QImage.Format_RGB888))
self.item.setPixmap(pix)
#if data is None and os.path.exists(fname):
# data = cv2imread_fix(fname)
# self.imgdata = data
#if data is not None:
# height, width, channel = data.shape
# bytesPerLine = 3 * width
# pix = QtGui.QPixmap()
# pix.convertFromImage(QtGui.QImage(data.data,
# width, height, bytesPerLine, QtGui.QImage.Format_RGB888))
# self.item.setPixmap(pix)
if self.darkenPixmap:
self.scene().setBackgroundBrush(QtGui.QColor(5, 5, 5))
self.item.setOpacity(0.2)
#self.item.setOpacity(0.2)
self.pyramid.setTileOpacity(0.2)
else:
self.scene().setBackgroundBrush(QtCore.Qt.darkGray)
self.item.setOpacity(1)
#self.item.setOpacity(1)
self.pyramid.setTileOpacity(1)
#else:
# self.item.setPixmap(QtGui.QPixmap())
else:
self.item.setPixmap(QtGui.QPixmap())
if self.mode == "OpticalScan":
for i, p in zip(self.dataset.fitindices, self.dataset.fitpoints):
p = self.dataset.mapToPixel(p, mode=microscope_mode, force=True)
fititem = FitPosIndicator(i+1, pos=p)
self.scene().addItem(fititem)
self.fititems.append(fititem)
self.pyramid.initScene()
if self.mode == "ParticleDetection" or self.mode == "ParticleAnalysis":
self.prepareAnalysis()
else:
......
This diff is collapsed.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment