Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
What's new
7
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Open sidebar
GEPARD
GEPARD
Commits
f1fe95fd
Commit
f1fe95fd
authored
Jul 21, 2019
by
Lars Bittrich
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
tranformation fix to separate the previous implicit image orientation in pixel sign values
parent
10e10e12
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
681 additions
and
652 deletions
+681
-652
analysis/database.py
analysis/database.py
+1
-1
dataset.py
dataset.py
+411
-386
helperfunctions.py
helperfunctions.py
+11
-0
zeissimporter.py
zeissimporter.py
+258
-265
No files found.
analysis/database.py
View file @
f1fe95fd
...
...
@@ -44,7 +44,7 @@ class DataBaseWindow(QtWidgets.QMainWindow):
self
.
path
=
os
.
path
.
join
(
Path
.
home
(),
'gepard'
,
'databases'
)
self
.
importPath
=
self
.
path
if
not
os
.
path
.
exists
(
self
.
path
):
os
.
m
k
dir
(
self
.
path
)
os
.
m
ake
dir
s
(
self
.
path
)
self
.
activeDatabase
=
None
self
.
activeSpectrum
=
None
self
.
activeSpectrumName
=
None
...
...
dataset.py
View file @
f1fe95fd
...
...
@@ -124,6 +124,9 @@ class DataSet(object):
self
.
zpositions
=
[]
# z-positions for optical scan
self
.
heightmap
=
None
self
.
zvalimg
=
None
self
.
coordinatetransform
=
None
# if imported form extern source coordinate system may be rotated
self
.
signx
=
1.
self
.
signy
=
-
1.
# parameters specifically for raman scan
self
.
pshift
=
None
# shift of raman scan position relative to image center
...
...
@@ -260,7 +263,14 @@ class DataSet(object):
def
getZval
(
self
,
pixelpos
):
assert
self
.
zvalimg
is
not
None
zp
=
self
.
zvalimg
[
round
(
pixelpos
[
1
]),
round
(
pixelpos
[
0
])]
i
,
j
=
int
(
round
(
pixelpos
[
1
])),
int
(
round
(
pixelpos
[
0
]))
if
i
>=
self
.
zvalimg
.
shape
[
0
]:
print
(
'error in getZval:'
,
self
.
zvalimg
.
shape
,
i
,
j
)
i
=
self
.
zvalimg
.
shape
[
0
]
-
1
if
j
>=
self
.
zvalimg
.
shape
[
1
]:
print
(
'error in getZval:'
,
self
.
zvalimg
.
shape
,
i
,
j
)
j
=
self
.
zvalimg
.
shape
[
1
]
-
1
zp
=
self
.
zvalimg
[
i
,
j
]
z0
,
z1
=
self
.
zpositions
.
min
(),
self
.
zpositions
.
max
()
return
zp
/
255.
*
(
z1
-
z0
)
+
z0
...
...
@@ -274,44 +284,59 @@ class DataSet(object):
assert
not
self
.
readin
p0
=
copy
(
self
.
lastpos
)
if
self
.
coordinatetransform
is
not
None
:
z
=
0.
if
len
(
p
)
<
3
else
p
[
2
]
T
,
pc
=
self
.
coordinatetransform
p
=
(
np
.
dot
(
np
.
array
([
p
[
0
],
p
[
1
],
z
])
-
pc
,
T
.
T
))
if
mode
==
'df'
:
p0
[
0
]
-=
self
.
imagedim_df
[
0
]
/
2
p0
[
1
]
+
=
self
.
imagedim_df
[
1
]
/
2
return
(
p
[
0
]
-
p0
[
0
])
/
self
.
pixelscale_df
,
(
p
0
[
1
]
-
p
[
1
])
/
self
.
pixelscale_df
p0
[
0
]
-=
self
.
signx
*
self
.
imagedim_df
[
0
]
/
2
p0
[
1
]
-
=
self
.
signy
*
self
.
imagedim_df
[
1
]
/
2
x
,
y
=
self
.
signx
*
(
p
[
0
]
-
p0
[
0
])
/
self
.
pixelscale_df
,
self
.
signy
*
(
p
[
1
]
-
p
0
[
1
])
/
self
.
pixelscale_df
elif
mode
==
'bf'
:
p0
[
0
]
-=
self
.
imagedim_bf
[
0
]
/
2
p0
[
1
]
+=
self
.
imagedim_bf
[
1
]
/
2
return
(
p
[
0
]
-
p0
[
0
])
/
self
.
pixelscale_bf
,
(
p0
[
1
]
-
p
[
1
])
/
self
.
pixelscale_bf
p0
[
0
]
-=
self
.
signx
*
self
.
imagedim_bf
[
0
]
/
2
p0
[
1
]
+=
self
.
signy
*
self
.
imagedim_bf
[
1
]
/
2
x
,
y
=
self
.
signx
*
(
p
[
0
]
-
p0
[
0
])
/
self
.
pixelscale_bf
,
self
.
signy
*
(
p
[
1
]
-
p0
[
1
])
/
self
.
pixelscale_bf
else
:
print
(
'mapToPixelMode not understood'
)
return
raise
ValueError
(
f
'mapToPixel mode:
{
mode
}
not understood'
)
return
x
,
y
def
mapToLength
(
self
,
pixelpos
,
mode
=
'df'
,
force
=
False
):
def
mapToLength
(
self
,
pixelpos
,
mode
=
'df'
,
force
=
False
,
returnz
=
False
):
if
not
force
:
assert
not
self
.
readin
p0
=
copy
(
self
.
lastpos
)
p0
[
0
]
+=
self
.
coordOffset
[
0
]
p0
[
1
]
+=
self
.
coordOffset
[
1
]
if
mode
==
'df'
:
p0
[
0
]
-=
self
.
imagedim_df
[
0
]
/
2
p0
[
1
]
+
=
self
.
imagedim_df
[
1
]
/
2
return
(
pixelpos
[
0
]
*
self
.
pixelscale_df
+
p0
[
0
]),
(
p0
[
1
]
-
pixelpos
[
1
]
*
self
.
pixelscale_df
)
p0
[
0
]
-=
self
.
signx
*
self
.
imagedim_df
[
0
]
/
2
p0
[
1
]
-
=
self
.
signy
*
self
.
imagedim_df
[
1
]
/
2
x
,
y
=
(
self
.
signx
*
pixelpos
[
0
]
*
self
.
pixelscale_df
+
p0
[
0
]),
(
p0
[
1
]
+
self
.
signy
*
pixelpos
[
1
]
*
self
.
pixelscale_df
)
elif
mode
==
'bf'
:
p0
[
0
]
-=
self
.
imagedim_bf
[
0
]
/
2
p0
[
1
]
+
=
self
.
imagedim_bf
[
1
]
/
2
return
(
pixelpos
[
0
]
*
self
.
pixelscale_bf
+
p0
[
0
]),
(
p0
[
1
]
-
pixelpos
[
1
]
*
self
.
pixelscale_bf
)
p0
[
0
]
-=
self
.
signx
*
self
.
imagedim_bf
[
0
]
/
2
p0
[
1
]
-
=
self
.
signy
*
self
.
imagedim_bf
[
1
]
/
2
x
,
y
=
(
self
.
signx
*
pixelpos
[
0
]
*
self
.
pixelscale_bf
+
p0
[
0
]),
(
p0
[
1
]
+
self
.
signy
*
pixelpos
[
1
]
*
self
.
pixelscale_bf
)
else
:
raise
ValueError
(
f
'mapToLength mode:
{
mode
}
not understood'
)
def
mapToLengthRaman
(
self
,
pixelpos
,
microscopeMode
=
'df'
,
noz
=
False
):
p0x
,
p0y
=
self
.
mapToLength
(
pixelpos
,
mode
=
microscopeMode
)
x
,
y
=
p0x
+
self
.
pshift
[
0
],
p0y
+
self
.
pshift
[
1
]
z
=
None
if
not
noz
:
if
(
returnz
and
self
.
zvalimg
is
not
None
)
or
self
.
coordinatetransform
is
not
None
:
z
=
self
.
mapHeight
(
x
,
y
)
z
+=
self
.
getZval
(
pixelpos
)
if
self
.
coordinatetransform
is
not
None
:
T
,
pc
=
self
.
coordinatetransform
x
,
y
,
z
=
(
np
.
dot
(
np
.
array
([
x
,
y
,
z
]),
T
)
+
pc
)
if
returnz
:
return
x
,
y
,
z
return
x
,
y
def
mapToLengthRaman
(
self
,
pixelpos
,
microscopeMode
=
'df'
,
noz
=
False
):
p0x
,
p0y
,
z
=
self
.
mapToLength
(
pixelpos
,
mode
=
microscopeMode
,
returnz
=
True
)
x
,
y
=
p0x
+
self
.
pshift
[
0
],
p0y
+
self
.
pshift
[
1
]
return
x
,
y
,
z
def
newProject
(
self
,
fname
):
...
...
helperfunctions.py
View file @
f1fe95fd
...
...
@@ -23,7 +23,16 @@ import numpy as np
import
cv2
import
os
try
:
from
skimage.io
import
imread
as
skimread
from
skimage.io
import
imsave
as
skimsave
except
ImportError
:
skimread
=
None
skimsave
=
None
def
cv2imread_fix
(
fname
,
flags
=
cv2
.
IMREAD_COLOR
):
if
skimread
is
not
None
:
return
skimread
(
fname
,
as_gray
=
(
flags
==
cv2
.
IMREAD_GRAYSCALE
))
with
open
(
fname
,
"rb"
)
as
fp
:
cont
=
fp
.
read
()
img
=
cv2
.
imdecode
(
np
.
fromstring
(
cont
,
dtype
=
np
.
uint8
),
flags
)
...
...
@@ -31,6 +40,8 @@ def cv2imread_fix(fname, flags=cv2.IMREAD_COLOR):
return
None
def
cv2imwrite_fix
(
fname
,
img
,
params
=
None
):
if
skimsave
is
not
None
:
skimsave
(
fname
,
img
)
pathname
,
ext
=
os
.
path
.
splitext
(
fname
)
if
params
is
None
:
ret
,
data
=
cv2
.
imencode
(
ext
,
img
)
...
...
zeissimporter.py
View file @
f1fe95fd
...
...
@@ -59,6 +59,10 @@ class ZeissImporter(QtWidgets.QDialog):
self
.
pexit
.
released
.
connect
(
self
.
reject
)
self
.
pconvert
.
setEnabled
(
False
)
self
.
xinvert
=
QtWidgets
.
QCheckBox
(
'Invert x-axis'
)
self
.
yinvert
=
QtWidgets
.
QCheckBox
(
'Invert y-axis'
)
self
.
zinvert
=
QtWidgets
.
QCheckBox
(
'Invert z-axis'
)
btnLayout
=
QtWidgets
.
QHBoxLayout
()
btnLayout
.
addStretch
()
btnLayout
.
addWidget
(
self
.
pconvert
)
...
...
@@ -77,22 +81,30 @@ class ZeissImporter(QtWidgets.QDialog):
vbox
.
addWidget
(
pointgroup
)
vbox
.
addLayout
(
blurlayout
)
vbox
.
addWidget
(
self
.
xinvert
)
vbox
.
addWidget
(
self
.
yinvert
)
vbox
.
addWidget
(
self
.
zinvert
)
vbox
.
addLayout
(
btnLayout
)
self
.
setLayout
(
vbox
)
def
readImportData
(
self
,
fname
):
path
=
os
.
path
.
split
(
fname
)[
0
]
self
.
zmapimgname
=
os
.
path
.
join
(
path
,
'3D.tif'
)
self
.
edfimgname
=
os
.
path
.
join
(
path
,
'EDF.tif'
)
xmlname
=
os
.
path
.
join
(
path
,
'3D.tif_metadata.xml'
)
self
.
edfimgname
,
self
.
zmapimgname
,
xmlname
=
''
,
''
,
''
for
name
in
os
.
listdir
(
path
):
if
name
.
lower
().
endswith
(
'_meta.xml'
):
xmlname
=
os
.
path
.
join
(
path
,
name
)
elif
name
.
lower
().
endswith
(
'_c1.tif'
):
self
.
edfimgname
=
os
.
path
.
join
(
path
,
name
)
elif
name
.
lower
().
endswith
(
'_c2.tif'
):
self
.
zmapimgname
=
os
.
path
.
join
(
path
,
name
)
errmsges
=
[]
if
not
os
.
path
.
exists
(
self
.
zmapimgname
):
errmsges
.
append
(
'Depth map image not found:
3D
.tif'
)
errmsges
.
append
(
'Depth map image not found:
NAME_c2
.tif'
)
if
not
os
.
path
.
exists
(
self
.
edfimgname
):
errmsges
.
append
(
'EDF image not found:
EDF
.tif'
)
errmsges
.
append
(
'EDF image not found:
NAME_c1
.tif'
)
if
not
os
.
path
.
exists
(
xmlname
):
errmsges
.
append
(
'XML metadata not found:
3D.tif
_meta
data
.xml'
)
errmsges
.
append
(
'XML metadata not found:
NAME
_meta.xml'
)
else
:
parser
=
make_parser
()
z
=
ZeissHandler
()
...
...
@@ -108,7 +120,7 @@ class ZeissImporter(QtWidgets.QDialog):
errmsges
.
append
(
'ZStack information missing or incomplete!'
)
if
len
(
errmsges
)
>
0
:
QtWidgets
.
QMessageBox
.
error
(
self
,
'Error!'
,
QtWidgets
.
QMessageBox
.
critical
(
self
,
'Error!'
,
'
\n
'
.
join
(
errmsges
),
QtWidgets
.
QMessageBox
.
Ok
,
QtWidgets
.
QMessageBox
.
Ok
)
...
...
@@ -117,6 +129,9 @@ class ZeissImporter(QtWidgets.QDialog):
self
.
region
=
z
.
region
self
.
zrange
=
z
.
zrange
self
.
markers
=
z
.
markers
print
(
self
.
region
)
print
(
self
.
zrange
)
print
(
self
.
markers
,
flush
=
True
)
return
True
@
QtCore
.
pyqtSlot
(
float
,
float
,
float
)
...
...
@@ -127,32 +142,33 @@ class ZeissImporter(QtWidgets.QDialog):
@
QtCore
.
pyqtSlot
()
def
convert
(
self
):
T
,
pc
,
zpc
,
accept
=
self
.
getTransform
()
if
accept
:
fname
=
QtWidgets
.
QFileDialog
.
getSaveFileName
(
self
,
'Create New GEPARD Project'
,
defaultPath
,
'*.pkl'
)[
0
]
if
fname
==
''
:
return
dataset
=
DataSet
(
fname
,
newProject
=
True
)
T
,
pc
,
zpc
=
self
.
getTransform
()
imgshape
,
warp_mat
=
self
.
convertZimg
(
dataset
,
T
,
pc
,
zpc
)
self
.
convertImage
(
dataset
,
warp_mat
)
self
.
convertZimg
(
dataset
,
T
,
pc
,
zpc
)
self
.
convertImage
(
dataset
)
dataset
.
save
()
self
.
gepardname
=
dataset
.
fname
self
.
accept
()
def
convertImage
(
self
,
dataset
,
warp_mat
):
def
convertImage
(
self
,
dataset
):
img
=
cv2imread_fix
(
self
.
edfimgname
)
img
=
cv2
.
warpAffine
(
img
,
warp_mat
,
img
.
shape
[:
2
][::
-
1
])
cv2imwrite_fix
(
dataset
.
getImageName
(),
img
)
def
convertZimg
(
self
,
dataset
,
T
,
pc
,
zpc
):
N
=
int
(
round
((
self
.
zrange
.
zn
-
self
.
zrange
.
z0
)
/
self
.
zrange
.
dz
))
dataset
.
zpositions
=
np
.
linspace
(
self
.
zrange
.
z0
,
self
.
zrange
.
zn
,
N
)
-
zpc
[
2
]
+
pc
[
2
]
N
=
int
(
round
(
abs
(
self
.
zrange
.
zn
-
self
.
zrange
.
z0
)
/
self
.
zrange
.
dz
))
z0
,
zn
=
self
.
zrange
.
z0
,
self
.
zrange
.
zn
if
zn
<
z0
:
zn
,
z0
=
z0
,
zn
dataset
.
zpositions
=
np
.
linspace
(
z0
,
zn
,
N
)
-
zpc
[
2
]
dataset
.
heightmap
=
np
.
zeros
(
3
)
dataset
.
signy
=
1.
zimg
=
cv2imread_fix
(
self
.
zmapimgname
,
cv2
.
IMREAD_GRAYSCALE
)
zmdist
=
zimg
.
mean
()
zm
=
zmdist
/
255.
*
(
self
.
zrange
.
zn
-
self
.
zrange
.
z0
)
+
self
.
zrange
.
z0
print
(
"zimg shape:"
,
zimg
.
shape
,
flush
=
True
)
radius
=
self
.
blurspinbox
.
value
()
blur
=
cv2
.
GaussianBlur
(
zimg
,
(
radius
,
radius
),
0
)
...
...
@@ -167,53 +183,32 @@ class ZeissImporter(QtWidgets.QDialog):
dataset
.
imagedim_bf
=
(
self
.
region
.
width
,
self
.
region
.
height
,
0.0
)
dataset
.
pixelscale_bf
=
pixelscale
# set image center as reference point in data set (transform from Zeiss)
p0
=
np
.
dot
((
np
.
array
([
self
.
region
.
centerx
,
self
.
region
.
centery
,
zm
])
-
zpc
),
T
)[:
2
]
+
pc
[:
2
]
dataset
.
coordinatetransform
=
T
,
pc
# set image center as reference point in data set (use Zeiss coordinates)
p0
=
np
.
array
([
self
.
region
.
centerx
,
self
.
region
.
centery
])
-
zpc
[:
2
]
dataset
.
readin
=
False
dataset
.
lastpos
=
p0
dataset
.
maxdim
=
p0
+
p0
# pixel triangle for coordinate warping transformation
srcTri
=
np
.
array
(
[[
0
,
0
],
[
zimg
.
shape
[
1
]
-
1
,
0
],
[
0
,
zimg
.
shape
[
0
]
-
1
]]
).
astype
(
np
.
float32
)
# upper left point (0,0) in Zeiss coordinates:
z0
=
np
.
array
([
self
.
region
.
centerx
-
self
.
region
.
width
/
2
,
self
.
region
.
centery
+
self
.
region
.
height
/
2
])
# transform pixel data to Zeiss coordinates
dstTri
=
np
.
array
([[
p
[
0
]
*
pixelscale
+
z0
[
0
],
z0
[
1
]
-
p
[
1
]
*
pixelscale
,
zm
]
for
p
in
srcTri
]).
astype
(
np
.
double
)
-
zpc
# transform to Raman coordinates
dstTri
=
np
.
dot
(
dstTri
,
T
)
+
pc
[
np
.
newaxis
,:]
# tilt blur image based on transformend z and adapt zpositions
x
=
np
.
linspace
(
0
,
1
,
blur
.
shape
[
1
])
y
=
np
.
linspace
(
0
,
1
,
blur
.
shape
[
0
])
x
,
y
=
np
.
meshgrid
(
x
,
y
)
zmap
=
x
*
(
dstTri
[
1
,
2
]
-
dstTri
[
0
,
2
])
+
y
*
(
dstTri
[
2
,
2
]
-
dstTri
[
0
,
2
])
+
\
(
zimg
*
((
self
.
zrange
.
zn
-
self
.
zrange
.
z0
)
/
255.
)
-
\
zmdist
*
((
self
.
zrange
.
zn
-
self
.
zrange
.
z0
)
/
255.
))
zmin
,
zmax
=
zmap
.
min
(),
zmap
.
max
()
dataset
.
zpositions
=
np
.
array
([
zmap
.
min
(),
zmap
.
max
()])
blur
=
(
zmap
-
zmin
)
*
(
255.
/
(
zmax
-
zmin
))
zmin
,
zmax
=
dataset
.
zpositions
.
min
(),
dataset
.
zpositions
.
max
()
blur
=
(
blur
)
*
(
255.
)
blur
[
blur
>
255.
]
=
255.
blur
[
np
.
isnan
(
blur
)]
=
0.
blur
=
np
.
uint8
(
blur
)
# transform triangle back to pixel
dstTri
=
np
.
array
([
dataset
.
mapToPixel
(
p
[:
2
])
for
p
in
dstTri
]).
astype
(
np
.
float32
)
warp_mat
=
cv2
.
getAffineTransform
(
srcTri
,
dstTri
)
blur
=
cv2
.
warpAffine
(
blur
,
warp_mat
,
zimg
.
shape
[::
-
1
])
zimgname
=
dataset
.
getZvalImageName
()
cv2imwrite_fix
(
zimgname
,
blur
)
return
zimg
.
shape
,
warp_mat
dataset
.
zvalimg
=
"saved"
def
getTransform
(
self
):
points
=
self
.
points
.
getPoints
()
pshift
=
self
.
ramanctrl
.
getRamanPositionShift
()
points
[:,
0
]
-=
pshift
[
0
]
points
[:,
1
]
-=
pshift
[
1
]
Parity
=
np
.
mat
(
np
.
diag
([
-
1.
if
self
.
xinvert
.
isChecked
()
else
1.
,
-
1.
if
self
.
yinvert
.
isChecked
()
else
1.
,
-
1.
if
self
.
zinvert
.
isChecked
()
else
1.
]))
zpoints
=
np
.
array
([
m
.
getPos
()
for
m
in
self
.
markers
],
dtype
=
np
.
double
)
pc
=
points
.
mean
(
axis
=
0
)
zpc
=
zpoints
.
mean
(
axis
=
0
)
...
...
@@ -233,11 +228,10 @@ class ZeissImporter(QtWidgets.QDialog):
# [-45°,45°] for all permutation of markers
permbest
=
None
pointsbest
=
None
for
perm
in
permutations
(
range
(
points
.
shape
[
0
])):
ppoints
=
points
[
perm
,:]
ppoints
=
points
[:,:].
copy
()
def
err
(
angles_shift
):
T
=
getRotMat
(
angles_shift
[:
3
]).
T
.
A
T
=
(
getRotMat
(
angles_shift
[:
3
]).
T
*
Parity
)
.
A
return
(
np
.
dot
(
zpoints
,
T
)
-
angles_shift
[
np
.
newaxis
,
3
:]
\
-
ppoints
).
ravel
()
...
...
@@ -246,25 +240,24 @@ class ZeissImporter(QtWidgets.QDialog):
bounds
=
(
np
.
array
([
-
np
.
pi
/
4
]
*
3
+
[
-
np
.
inf
]
*
3
),
np
.
array
([
np
.
pi
/
4
]
*
3
+
[
np
.
inf
]
*
3
)),
method
=
'dogbox'
)
if
permbest
is
None
or
\
permbest
.
cost
>
opt
.
cost
:
print
(
"Current best permutation:"
,
perm
,
flush
=
True
)
permbest
=
opt
pointsbest
=
ppoints
optangles
=
permbest
.
x
[:
3
]
shift
=
permbest
.
x
[
3
:]
T
=
getRotMat
(
optangles
).
T
.
A
T
=
(
getRotMat
(
optangles
).
T
*
Parity
)
.
A
e
=
(
np
.
dot
(
zpoints
,
T
)
-
shift
[
np
.
newaxis
,:]
-
pointsbest
)
print
(
"Transformation angles:"
,
optangles
,
flush
=
True
)
print
(
"Transformation shift:"
,
shift
,
flush
=
True
)
print
(
"Transformation err:"
,
e
,
flush
=
True
)
d
=
np
.
linalg
.
norm
(
e
,
axis
=
1
)
accept
=
True
if
np
.
any
(
d
>
1.
):
QtWidgets
.
QMessageBox
.
warning
(
self
,
'Warning!'
,
ret
=
QtWidgets
.
QMessageBox
.
warning
(
self
,
'Warning!'
,
f
'Transformation residuals are large:
{
d
}
'
,
QtWidgets
.
QMessageBox
.
Ok
,
QtWidgets
.
QMessageBox
.
Ok
|
QtWidgets
.
QMessageBox
.
Cancel
,
QtWidgets
.
QMessageBox
.
Ok
)
return
T
,
pc
-
shift
,
zpc
if
ret
==
QtWidgets
.
QMessageBox
.
Cancel
:
accept
=
False
return
T
,
pc
-
shift
,
zpc
,
accept
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment