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

-initial integration of ScenePyramid from GepardEvaluation into Gepard

parent 1fff5fef
......@@ -135,6 +135,9 @@ class DataSet(object):
self.coordinatetransform = None # if imported form extern source coordinate system may be rotated
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
......@@ -169,6 +172,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:
......@@ -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:
......@@ -110,6 +111,12 @@ def legacyConversion(dset, recreatefullimage=False):
removeLegacyAttributes(dset)
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)
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))
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)
zval = fullzval
else:
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
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.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)
......
This diff is collapsed.
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