Commit 5a441838 authored by Robert's avatar Robert Committed by Robert Ohmacht

-fixes tiles not being generated if full image is only slightly bigger than a...

-fixes tiles not being generated if full image is only slightly bigger than a multiple of tile size and scanned images not reaching into all tiles, so full image would not be fully covered by tiles
parent 46a56d63
...@@ -20,9 +20,14 @@ along with this program, see COPYING. ...@@ -20,9 +20,14 @@ along with this program, see COPYING.
If not, see <https://www.gnu.org/licenses/>. If not, see <https://www.gnu.org/licenses/>.
""" """
class InvalidParticleError(Exception): class InvalidParticleError(Exception):
pass pass
class NotConnectedContoursError(Exception): class NotConnectedContoursError(Exception):
pass pass
\ No newline at end of file
class TileSizeError(Exception):
pass
...@@ -125,6 +125,19 @@ def removeSrcTiles(names, path): ...@@ -125,6 +125,19 @@ def removeSrcTiles(names, path):
def loadAndPasteImage(srcnames, pyramid, fullzval, width, height, def loadAndPasteImage(srcnames, pyramid, fullzval, width, height,
rotationvalue, p0, p1, p, background=None): rotationvalue, p0, p1, p, background=None):
"""
:param list of str srcnames: list of stacked scan files to merge
:param ScenePyramid pyramid: the scene pyramid
:param numpy.ndarray fullzval: full size zval image
:param float width: width of camera
:param float height: height camera
:param float rotationvalue: angle of camera
:param list of float p0: (min x; max y) of scan tile positions
:param list of float p1: (max x; min y) of scan tile positions
:param list of float p: position of current scan tile
:param numpy.ndarray background:
:return:
"""
colimgs = [] colimgs = []
for name in srcnames: for name in srcnames:
curImg = cv2imread_fix(name) curImg = cv2imread_fix(name)
...@@ -680,8 +693,8 @@ class OpticalScan(QtWidgets.QWidget): ...@@ -680,8 +693,8 @@ class OpticalScan(QtWidgets.QWidget):
self.pyramid.resetScene() self.pyramid.resetScene()
self.view.blockUI() self.view.blockUI()
grid = np.asarray(self.dataset.grid) grid = np.asarray(self.dataset.grid)
p0 = [grid[:,0].min(), grid[:,1].max()] p0 = [grid[:, 0].min(), grid[:, 1].max()]
p1 = [grid[:,0].max(), grid[:,1].min()] p1 = [grid[:, 0].max(), grid[:, 1].min()]
self.dataset.lastpos = p0 self.dataset.lastpos = p0
self.dataset.maxdim = p0 + p1 self.dataset.maxdim = p0 + p1
self.dataset.mode = "opticalscan" self.dataset.mode = "opticalscan"
...@@ -698,7 +711,7 @@ class OpticalScan(QtWidgets.QWidget): ...@@ -698,7 +711,7 @@ class OpticalScan(QtWidgets.QWidget):
except queue.Empty: except queue.Empty:
i = -1 i = -1
if i>=0: if i >= 0:
Ngrid = len(self.dataset.grid) Ngrid = len(self.dataset.grid)
names = [] names = []
...@@ -721,17 +734,17 @@ class OpticalScan(QtWidgets.QWidget): ...@@ -721,17 +734,17 @@ class OpticalScan(QtWidgets.QWidget):
removeSrcTiles(names, self.dataset.getScanPath()) removeSrcTiles(names, self.dataset.getScanPath())
self.progressbar.setValue(i+1) self.progressbar.setValue(i+1)
if i>3: if i > 3:
timerunning = time()-self.starttime timerunning = time()-self.starttime
ttot = timerunning*Ngrid/(i+1) ttot = timerunning*Ngrid/(i+1)
time2go = ttot - timerunning time2go = ttot - timerunning
self.progresstime.setText(self.timelabeltext + str(datetime.timedelta(seconds=round(time2go)))) self.progresstime.setText(self.timelabeltext + str(datetime.timedelta(seconds=round(time2go))))
# reload image in sampleview, calls loadPixmap # reload image in sampleview, calls loadPixmap
# not needed anymore as the scene gets manipulated directly via self.pyramid # not needed anymore as the scene gets manipulated directly via self.pyramid
#self.imageUpdate.emit(self.view.microscopeMode) # self.imageUpdate.emit(self.view.microscopeMode)
if i==Ngrid-1: 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.dataset.saveZvalImg()
self.process.join() self.process.join()
self.dataqueue.close() self.dataqueue.close()
...@@ -752,4 +765,4 @@ class OpticalScan(QtWidgets.QWidget): ...@@ -752,4 +765,4 @@ class OpticalScan(QtWidgets.QWidget):
def closeEvent(self, event): def closeEvent(self, event):
self.backGroundManager.close() self.backGroundManager.close()
event.accept() event.accept()
\ No newline at end of file
...@@ -26,6 +26,7 @@ import cv2 ...@@ -26,6 +26,7 @@ import cv2
import math import math
import copy import copy
import numpy as np import numpy as np
from .errors import TileSizeError
from .helperfunctions import cv2imread_fix, cv2imwrite_fix from .helperfunctions import cv2imread_fix, cv2imwrite_fix
from PIL import Image from PIL import Image
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5 import QtCore, QtGui, QtWidgets
...@@ -439,6 +440,7 @@ class ScenePyramid: ...@@ -439,6 +440,7 @@ class ScenePyramid:
for i in self.datasetParams: for i in self.datasetParams:
setattr(self, i, pyramid_params[i]) setattr(self, i, pyramid_params[i])
self.tileWorkingSets = copy.deepcopy(self.tileSets) self.tileWorkingSets = copy.deepcopy(self.tileSets)
self.fixMissingTiles()
self.setMicroscopeMode(self.dataset.imagescanMode) self.setMicroscopeMode(self.dataset.imagescanMode)
...@@ -448,6 +450,7 @@ class ScenePyramid: ...@@ -448,6 +450,7 @@ class ScenePyramid:
:return: :return:
""" """
pyramid_params = {} pyramid_params = {}
self.fixMissingTiles()
for i in self.datasetParams: for i in self.datasetParams:
pyramid_params[i] = getattr(self, i) pyramid_params[i] = getattr(self, i)
...@@ -625,17 +628,26 @@ class ScenePyramid: ...@@ -625,17 +628,26 @@ class ScenePyramid:
except OSError: except OSError:
tile = None tile = None
# as full image size might not be divisible by tile dimensions, we have to determine correct tile size
# tile numbering is 0 based
scaling = self.scalingFactor ** slice_nr
max_x, max_y = self.tileDim
fiw = scaling * self.fullImageWidth
fih = scaling * self.fullImageHeight
tile_w = int(math.ceil(max(min(max_x, fiw - i * max_x), 0)))
tile_h = int(math.ceil(max(min(max_y, fih - j * max_y), 0)))
# tile does exists but its shape is not correct
if tile is not None and (tile_h, tile_w) != tile.shape[:2]:
# this should not happen
raise TileSizeError(
f"tile should have dim ({tile_w}; {tile_h}), but has dim ({tile.shape[1]}; {tile.shape[0]})"
)
# tile does not even exist, create
if tile is None: if tile is None:
# as full image size might not be divisible by tile dimensions, we have to determine correct tile size tile = np.zeros((tile_h, tile_w, 3), np.uint8)
# tile numbering is 0 based
scaling = self.scalingFactor ** slice_nr
max_x, max_y = self.tileDim
fiw = scaling * self.fullImageWidth
fih = scaling * self.fullImageHeight
x = int(math.ceil(max(min(max_x, fiw - i * max_x), 0)))
y = int(math.ceil(max(min(max_y, fih - j * max_y), 0)))
tile = np.zeros((y, x, 3), np.uint8)
return tile return tile
def createFullImage(self): def createFullImage(self):
...@@ -860,6 +872,35 @@ class ScenePyramid: ...@@ -860,6 +872,35 @@ class ScenePyramid:
self.opacity = opacity self.opacity = opacity
[tile.setOpacity(opacity) for tile in self.currentTiles] [tile.setOpacity(opacity) for tile in self.currentTiles]
def fixMissingTiles(self):
"""
:return:
"""
scaling = 1
tile_width, tile_height = self.tileDim
for slice in range(0, self.maxSliceNumber + 1):
img_width = math.ceil(scaling * self.fullImageWidth)
img_height = math.ceil(scaling * self.fullImageHeight)
# tiles in x direction
col_tile_count = math.ceil(img_width / tile_width)
# tiles in y direction
row_tile_count = math.ceil(img_height / tile_height)
for i in range(0, col_tile_count):
w_tile = (tile_width if i + 1 < col_tile_count else img_width % tile_width)
if i not in self.tileSets[slice]:
self.tileSets[slice][i] = {}
self.tileWorkingSets[slice][i] = {}
col = self.tileSets[slice][i]
for j in range(0, row_tile_count):
h_tile = (tile_height if j + 1 < row_tile_count else img_height % tile_height)
if j not in col or col[j]["dimensions"] != (w_tile, h_tile):
tile = self.readViewTile(slice, i, j)
self.saveViewTile(tile, slice, i, j)
scaling *= self.scalingFactor
def destruct(self): def destruct(self):
""" """
removes all graphics items from scene removes all graphics items from scene
......
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