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
Josef Brandt
Subsampling
Commits
064d26eb
Commit
064d26eb
authored
Mar 09, 2020
by
Josef Brandt
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Dataset can be loaded in mainView
parent
c8650c49
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
561 additions
and
144 deletions
+561
-144
geometricMethods.py
geometricMethods.py
+68
-15
gui/filterView.py
gui/filterView.py
+150
-12
gui/mainView.py
gui/mainView.py
+10
-0
gui/measureModes.py
gui/measureModes.py
+10
-17
helpers.py
helpers.py
+104
-15
methods.py
methods.py
+0
-24
subsampling.py
subsampling.py
+53
-47
tests/test_geometricMethods.py
tests/test_geometricMethods.py
+47
-8
tests/test_helpers.py
tests/test_helpers.py
+119
-6
No files found.
geometricMethods.py
View file @
064d26eb
import
numpy
as
np
from
itertools
import
combinations
from
methods
import
BoxSelectionSubsamplingMethod
from
helpers
import
box_contains_contour
from
methods
import
SubsamplingMethod
import
sys
sys
.
path
.
append
(
"C://Users//xbrjos//Desktop//Python"
)
from
gepard
import
dataset
class
BoxSelectionSubsamplingMethod
(
SubsamplingMethod
):
def
__init__
(
self
,
*
args
):
super
(
BoxSelectionSubsamplingMethod
,
self
).
__init__
(
*
args
)
self
.
filterDiameter
:
float
=
500
self
.
offset
:
tuple
=
(
0
,
0
)
@
property
def
filterArea
(
self
)
->
float
:
return
np
.
pi
*
(
self
.
filterDiameter
/
2
)
**
2
def
apply_subsampling_method
(
self
)
->
tuple
:
subParticles
:
list
=
[]
topLefts
:
list
=
self
.
get_topLeft_of_boxes
()
boxSize
=
self
.
boxSize
for
particle
in
self
.
particleContainer
.
particles
:
for
topLeft
in
topLefts
:
if
box_overlaps_contour
(
topLeft
,
(
boxSize
,
boxSize
),
particle
.
contour
,
self
.
offset
):
subParticles
.
append
(
particle
)
return
self
.
fraction
,
subParticles
def
get_topLeft_of_boxes
(
self
)
->
list
:
raise
NotImplementedError
class
BoxSelectionCreator
(
object
):
def
__init__
(
self
,
dataset
:
dataset
.
DataSet
):
super
(
BoxSelectionCreator
,
self
).
__init__
()
self
.
dataset
:
dataset
.
DataSet
=
dataset
def
get_crossBoxSelectors_for_fraction
(
self
,
desiredFraction
:
float
)
->
list
:
"""
Creates CrossBoxSelectors that fullfil the desired fraction criterium.
:param desiredFraction:
:return:
"""
crossBoxSelectors
=
[]
offset
,
diameter
,
widthHeight
=
self
.
get_filterDimensions_from_dataset
()
# for numBoxesAcross in [3, 5]:
# newBoxSelector: CrossBoxSelector = CrossBoxSelector(self.dataset.particleContainer, desiredFraction)
# newBoxSelector.filterDiameter = diameter
# newBoxSelector.offset = offset
# newBoxSelector.numBoxesAcross = numBoxesAcross
#
# crossBoxSelectors.append(newBoxSelector)
return
crossBoxSelectors
class
CrossBoxSelector
(
BoxSelectionSubsamplingMethod
):
...
...
@@ -41,7 +94,7 @@ class CrossBoxSelector(BoxSelectionSubsamplingMethod):
:return float:
"""
alpha
:
float
=
np
.
deg2rad
(
135
)
r
:
float
=
self
.
filter
Size
/
2
r
:
float
=
self
.
filter
Diameter
/
2
d
:
float
=
(
self
.
numBoxesAcross
-
1
)
*
r
/
self
.
numBoxesAcross
# 2/3*r for numAcross = 3, 4/5*r numAcross = 5
delta
:
float
=
np
.
arcsin
((
np
.
sin
(
alpha
)
*
d
)
/
r
)
gamma
:
float
=
np
.
pi
-
alpha
-
delta
...
...
@@ -69,11 +122,11 @@ class CrossBoxSelector(BoxSelectionSubsamplingMethod):
return
self
.
_get_box_starts
(
boxSize
)
def
_get_box_starts
(
self
,
boxSize
:
float
)
->
list
:
maxBoxSize
:
float
=
self
.
filter
Size
/
self
.
numBoxesAcross
maxBoxSize
:
float
=
self
.
filter
Diameter
/
self
.
numBoxesAcross
assert
maxBoxSize
>=
boxSize
tileStarts
:
list
=
[]
for
i
in
range
(
self
.
numBoxesAcross
):
start
:
float
=
i
*
self
.
filter
Size
/
self
.
numBoxesAcross
+
(
maxBoxSize
-
boxSize
)
/
2
start
:
float
=
i
*
self
.
filter
Diameter
/
self
.
numBoxesAcross
+
(
maxBoxSize
-
boxSize
)
/
2
tileStarts
.
append
(
start
)
return
tileStarts
...
...
@@ -102,7 +155,7 @@ class SpiralSelector(BoxSelectionSubsamplingMethod):
Calculates the topLeft-points (x, y) of all measure boxes
The method uses an approximation for the spiral and is not purely accurate.
:return list:"""
filterCenter
:
tuple
=
self
.
filter
Size
/
2
-
self
.
boxSize
/
2
,
self
.
filter
Size
/
2
-
self
.
boxSize
/
2
filterCenter
:
tuple
=
self
.
filter
Diameter
/
2
-
self
.
boxSize
/
2
,
self
.
filter
Diameter
/
2
-
self
.
boxSize
/
2
slope
=
self
.
spiralSlope
theta
:
float
=
0
boxDistance
=
self
.
boxSize
*
1.1
...
...
@@ -123,25 +176,25 @@ class SpiralSelector(BoxSelectionSubsamplingMethod):
This function moves and scales the topLeft-Points so that all measure boxes lie within the filter limits.
:return list:
"""
xCoords
:
np
.
array
=
np
.
array
([
float
(
point
[
0
])
for
point
in
topLefts
])
-
self
.
filter
Size
/
2
yCoords
:
np
.
array
=
np
.
array
([
float
(
point
[
1
])
for
point
in
topLefts
])
-
self
.
filter
Size
/
2
xCoords
:
np
.
nd
array
=
np
.
array
([
float
(
point
[
0
])
for
point
in
topLefts
])
-
self
.
filter
Diameter
/
2
yCoords
:
np
.
nd
array
=
np
.
array
([
float
(
point
[
1
])
for
point
in
topLefts
])
-
self
.
filter
Diameter
/
2
xCoordsBoxMiddles
:
np
.
array
=
xCoords
+
self
.
boxSize
/
2
yCoordsBoxMiddles
:
np
.
array
=
yCoords
+
self
.
boxSize
/
2
xCoordsBoxMiddles
:
np
.
nd
array
=
xCoords
+
self
.
boxSize
/
2
yCoordsBoxMiddles
:
np
.
nd
array
=
yCoords
+
self
.
boxSize
/
2
lastBoxCenter
:
tuple
=
(
xCoordsBoxMiddles
[
-
1
],
yCoordsBoxMiddles
[
-
1
])
distanceLastCenter
:
float
=
np
.
linalg
.
norm
(
lastBoxCenter
)
maxDistanceInLastBox
:
float
=
self
.
_get_max_distance_of_boxCenter_to_center
(
lastBoxCenter
)
halfBoxDistance
:
float
=
maxDistanceInLastBox
-
distanceLastCenter
desiredDistanceTotal
:
float
=
self
.
filter
Height
/
2
desiredDistanceTotal
:
float
=
self
.
filter
Diameter
/
2
desiredDistanceCenter
:
float
=
desiredDistanceTotal
-
halfBoxDistance
scaleFactor
:
float
=
desiredDistanceCenter
/
distanceLastCenter
xCoordsBoxMiddles
*=
scaleFactor
yCoordsBoxMiddles
*=
scaleFactor
xCoords
=
xCoordsBoxMiddles
+
(
self
.
filter
Size
-
self
.
boxSize
)
/
2
yCoords
=
yCoordsBoxMiddles
+
(
self
.
filter
Size
-
self
.
boxSize
)
/
2
xCoords
=
xCoordsBoxMiddles
+
(
self
.
filter
Diameter
-
self
.
boxSize
)
/
2
yCoords
=
yCoordsBoxMiddles
+
(
self
.
filter
Diameter
-
self
.
boxSize
)
/
2
newTopLefts
=
zip
(
np
.
round
(
xCoords
),
np
.
round
(
yCoords
))
return
list
(
tuple
(
newTopLefts
))
...
...
@@ -155,12 +208,12 @@ class SpiralSelector(BoxSelectionSubsamplingMethod):
"""
center
=
np
.
array
(
center
)
boxSize
=
self
.
boxSize
coords
:
np
.
array
=
np
.
array
([[
boxCenter
[
0
]
-
0.5
*
boxSize
,
boxCenter
[
1
]
-
0.5
*
boxSize
],
coords
:
np
.
nd
array
=
np
.
array
([[
boxCenter
[
0
]
-
0.5
*
boxSize
,
boxCenter
[
1
]
-
0.5
*
boxSize
],
[
boxCenter
[
0
]
+
0.5
*
boxSize
,
boxCenter
[
1
]
-
0.5
*
boxSize
],
[
boxCenter
[
0
]
-
0.5
*
boxSize
,
boxCenter
[
1
]
+
0.5
*
boxSize
],
[
boxCenter
[
0
]
+
0.5
*
boxSize
,
boxCenter
[
1
]
+
0.5
*
boxSize
]])
distances
:
np
.
array
=
np
.
linalg
.
norm
(
coords
-
center
,
axis
=
1
)
distances
:
np
.
nd
array
=
np
.
linalg
.
norm
(
coords
-
center
,
axis
=
1
)
return
np
.
max
(
distances
)
def
_get_xy_at_angle
(
self
,
theta
:
float
,
centerXY
:
tuple
=
(
0
,
0
))
->
tuple
:
...
...
gui/filterView.py
View file @
064d26eb
from
PyQt5
import
QtGui
,
QtWidgets
,
QtCore
import
sys
sys
.
path
.
append
(
"C://Users//xbrjos//Desktop//Python"
)
import
gepard
from
gepard
import
dataset
import
helpers
class
FilterView
(
QtWidgets
.
QGraphicsView
):
...
...
@@ -7,6 +12,8 @@ class FilterView(QtWidgets.QGraphicsView):
super
(
FilterView
,
self
).
__init__
()
self
.
setWindowTitle
(
'FilterView'
)
self
.
dataset
:
dataset
.
DataSet
=
None
scene
=
QtWidgets
.
QGraphicsScene
(
self
)
scene
.
setItemIndexMethod
(
QtWidgets
.
QGraphicsScene
.
NoIndex
)
scene
.
setBackgroundBrush
(
QtCore
.
Qt
.
darkGray
)
...
...
@@ -14,37 +21,124 @@ class FilterView(QtWidgets.QGraphicsView):
self
.
setCacheMode
(
QtWidgets
.
QGraphicsView
.
CacheBackground
)
self
.
setViewportUpdateMode
(
QtWidgets
.
QGraphicsView
.
BoundingRectViewportUpdate
)
self
.
setRenderHint
(
QtGui
.
QPainter
.
Antialiasing
)
self
.
setTransformationAnchor
(
QtWidgets
.
QGraphicsView
.
AnchorUnderMouse
)
self
.
setResizeAnchor
(
QtWidgets
.
QGraphicsView
.
AnchorUnderMouse
)
self
.
drag
=
None
self
.
filter
=
FilterGraphItem
()
self
.
filter
:
FilterGraphItem
=
FilterGraphItem
()
self
.
scene
().
addItem
(
self
.
filter
)
self
.
measuringBoxes
:
list
=
[]
self
.
contourItems
:
list
=
[]
def
update_measure_boxes
(
self
,
viewItems
:
lis
t
)
->
None
:
def
update_measure_boxes
(
self
,
topLefts
:
list
,
boxSize
:
floa
t
)
->
None
:
self
.
_remove_measure_boxes
()
for
item
in
viewItems
:
self
.
measuringBoxes
.
append
(
item
)
self
.
scene
().
addItem
(
item
)
offset
=
self
.
filter
.
circleOffset
for
x
,
y
in
topLefts
:
newBox
:
MeasureBoxGraphItem
=
MeasureBoxGraphItem
(
x
+
offset
[
0
],
y
+
offset
[
1
],
boxSize
,
boxSize
)
self
.
measuringBoxes
.
append
(
newBox
)
self
.
scene
().
addItem
(
newBox
)
self
.
_update_measured_contours
()
def
_remove_measure_boxes
(
self
)
->
None
:
for
item
in
self
.
measuringBoxes
:
self
.
scene
().
removeItem
(
item
)
self
.
measuringBoxes
=
[]
def
load_and_update_from_dataset
(
self
,
fname
:
str
)
->
None
:
self
.
dataset
=
dataset
.
loadData
(
fname
)
offset
,
diameter
,
widthHeight
=
helpers
.
get_filterDimensions_from_dataset
(
self
.
dataset
)
offsetx
=
helpers
.
convert_length_to_pixels
(
self
.
dataset
,
offset
[
0
])
offsety
=
helpers
.
convert_length_to_pixels
(
self
.
dataset
,
offset
[
1
])
diameter
=
helpers
.
convert_length_to_pixels
(
self
.
dataset
,
diameter
)
width
=
helpers
.
convert_length_to_pixels
(
self
.
dataset
,
widthHeight
[
0
])
height
=
helpers
.
convert_length_to_pixels
(
self
.
dataset
,
widthHeight
[
1
])
self
.
filter
.
update_filterSize
(
width
,
height
,
diameter
,
(
offsetx
,
offsety
))
self
.
_update_particle_contours
()
self
.
_fit_to_window
()
def
_update_particle_contours
(
self
)
->
None
:
self
.
_remove_particle_contours
()
if
self
.
dataset
is
not
None
:
for
particle
in
self
.
dataset
.
particleContainer
.
particles
:
newContour
:
ParticleContour
=
ParticleContour
(
particle
.
contour
)
self
.
scene
().
addItem
(
newContour
)
self
.
contourItems
.
append
(
newContour
)
def
_remove_particle_contours
(
self
)
->
None
:
for
cntItem
in
self
.
contourItems
:
self
.
scene
().
removeItem
(
cntItem
)
self
.
contourItems
=
[]
def
_update_measured_contours
(
self
)
->
None
:
# offset = self.filter.circleOffset
# offset = (-offset[0], -offset[1])
offset
=
(
0
,
0
)
for
contourItem
in
self
.
contourItems
:
contourItem
.
isMeasured
=
False
for
measBox
in
self
.
measuringBoxes
:
topLeftXY
=
(
measBox
.
posX
,
measBox
.
posY
)
boxWidthHeight
=
(
measBox
.
width
,
measBox
.
height
)
if
helpers
.
box_overlaps_contour
(
topLeftXY
,
boxWidthHeight
,
contourItem
.
polygon
,
offset
=
offset
):
contourItem
.
isMeasured
=
True
contourItem
.
update
()
break
def
wheelEvent
(
self
,
event
:
QtGui
.
QWheelEvent
)
->
None
:
factor
:
float
=
1.01
**
(
event
.
angleDelta
().
y
()
/
8
)
newScale
:
float
=
self
.
filter
.
scale
()
*
factor
self
.
scale
(
newScale
,
newScale
)
def
mousePressEvent
(
self
,
event
):
if
event
.
button
()
==
QtCore
.
Qt
.
MiddleButton
:
self
.
drag
=
event
.
pos
()
def
mouseMoveEvent
(
self
,
event
):
if
self
.
drag
is
not
None
:
p0
=
event
.
pos
()
move
=
self
.
drag
-
p0
self
.
horizontalScrollBar
().
setValue
(
move
.
x
()
+
self
.
horizontalScrollBar
().
value
())
self
.
verticalScrollBar
().
setValue
(
move
.
y
()
+
self
.
verticalScrollBar
().
value
())
self
.
drag
=
p0
def
mouseReleaseEvent
(
self
,
event
:
QtGui
.
QMouseEvent
)
->
None
:
self
.
drag
=
None
def
_fit_to_window
(
self
)
->
None
:
brect
=
self
.
scene
().
itemsBoundingRect
()
self
.
fitInView
(
0
,
0
,
brect
.
width
(),
brect
.
height
(),
QtCore
.
Qt
.
KeepAspectRatio
)
class
FilterGraphItem
(
QtWidgets
.
QGraphicsItem
):
"""
The Graphical Representation of the actual filter
"""
def
__init__
(
self
,
filterWidth
:
float
=
500
,
filterHeight
:
float
=
500
):
def
__init__
(
self
,
filterWidth
:
float
=
500
,
filterHeight
:
float
=
500
,
diameter
:
float
=
500
):
super
(
FilterGraphItem
,
self
).
__init__
()
self
.
width
=
filterWidth
self
.
height
=
filterHeight
self
.
width
:
float
=
filterWidth
self
.
height
:
float
=
filterHeight
self
.
diameter
:
float
=
diameter
self
.
circleOffset
:
tuple
=
(
0
,
0
)
self
.
setPos
(
0
,
0
)
self
.
rect
=
QtCore
.
QRectF
(
0
,
0
,
self
.
width
,
self
.
height
)
self
.
circleRect
=
QtCore
.
QRectF
(
self
.
circleOffset
[
0
],
self
.
circleOffset
[
1
],
self
.
diameter
,
self
.
diameter
)
def
boundingRect
(
self
)
->
QtCore
.
QRectF
:
return
self
.
rect
def
update_filterSize
(
self
,
width
:
float
,
height
:
float
,
diameter
:
float
,
offset
:
tuple
)
->
None
:
self
.
width
=
width
self
.
height
=
height
self
.
rect
=
QtCore
.
QRectF
(
0
,
0
,
self
.
width
,
self
.
height
)
self
.
diameter
=
diameter
self
.
circleOffset
=
offset
self
.
circleRect
=
QtCore
.
QRectF
(
self
.
circleOffset
[
0
],
self
.
circleOffset
[
1
],
self
.
diameter
,
self
.
diameter
)
def
paint
(
self
,
painter
:
QtGui
.
QPainter
,
option
,
widget
)
->
None
:
painter
.
setPen
(
QtCore
.
Qt
.
black
)
painter
.
setBrush
(
QtCore
.
Qt
.
white
)
...
...
@@ -52,15 +146,16 @@ class FilterGraphItem(QtWidgets.QGraphicsItem):
painter
.
setPen
(
QtCore
.
Qt
.
darkGray
)
painter
.
setBrush
(
QtCore
.
Qt
.
lightGray
)
painter
.
drawEllipse
(
self
.
r
ect
)
painter
.
drawEllipse
(
self
.
circleR
ect
)
class
MeasureBoxGraphItem
(
QtWidgets
.
QGraphicsItem
):
"""
Displays a box in which particles will be measured
"""
def
__init__
(
self
,
posX
:
float
=
50
,
posY
:
float
=
50
,
width
:
float
=
50
,
height
:
float
=
50
)
->
None
:
def
__init__
(
self
,
posX
:
float
=
50
,
posY
:
float
=
50
,
width
:
float
=
50
,
height
:
float
=
50
)
->
None
:
super
(
MeasureBoxGraphItem
,
self
).
__init__
()
self
.
setZValue
(
5
)
self
.
posX
:
float
=
posX
self
.
posY
:
float
=
posY
self
.
height
:
float
=
height
...
...
@@ -74,6 +169,49 @@ class MeasureBoxGraphItem(QtWidgets.QGraphicsItem):
return
self
.
rect
def
paint
(
self
,
painter
,
option
,
widget
)
->
None
:
painter
.
setBrush
(
Qt
Core
.
Qt
.
green
)
painter
.
setBrush
(
Qt
Gui
.
QColor
(
0
,
255
,
0
,
180
)
)
painter
.
setPen
(
QtCore
.
Qt
.
darkGreen
)
painter
.
drawRects
(
self
.
rect
)
\ No newline at end of file
painter
.
drawRects
(
self
.
rect
)
class
ParticleContour
(
QtWidgets
.
QGraphicsItem
):
def
__init__
(
self
,
contourData
,
pos
=
(
0
,
0
))
->
None
:
super
(
ParticleContour
,
self
).
__init__
()
self
.
setZValue
(
1
)
self
.
setPos
(
pos
[
0
],
pos
[
1
])
self
.
brect
:
QtCore
.
QRectF
=
QtCore
.
QRectF
(
0
,
0
,
1
,
1
)
self
.
isMeasured
:
bool
=
False
# Wether the particle overlaps with a measuring box or nt
self
.
contourData
=
contourData
self
.
polygon
=
None
self
.
getBrectAndPolygon
()
def
getBrectAndPolygon
(
self
)
->
None
:
"""
Calculates the bounding rect (needed for drawing the QGraphicsView) and converts the contourdata to a polygon.
:return:
"""
self
.
polygon
=
QtGui
.
QPolygonF
()
x0
=
self
.
contourData
[:,
0
,
0
].
min
()
x1
=
self
.
contourData
[:,
0
,
0
].
max
()
y0
=
self
.
contourData
[:,
0
,
1
].
min
()
y1
=
self
.
contourData
[:,
0
,
1
].
max
()
for
point
in
self
.
contourData
:
self
.
polygon
.
append
(
QtCore
.
QPointF
(
point
[
0
,
0
],
point
[
0
,
1
]))
self
.
brect
.
setCoords
(
x0
,
y0
,
x1
,
y1
)
def
boundingRect
(
self
)
->
QtCore
.
QRectF
:
return
self
.
brect
def
paint
(
self
,
painter
,
option
,
widget
)
->
None
:
if
self
.
polygon
is
not
None
:
if
self
.
isMeasured
:
painter
.
setPen
(
QtCore
.
Qt
.
darkRed
)
painter
.
setBrush
(
QtCore
.
Qt
.
red
)
else
:
painter
.
setPen
(
QtCore
.
Qt
.
darkCyan
)
painter
.
setBrush
(
QtCore
.
Qt
.
cyan
)
painter
.
drawPolygon
(
self
.
polygon
)
gui/mainView.py
View file @
064d26eb
...
...
@@ -17,10 +17,14 @@ class MainView(QtWidgets.QWidget):
self
.
activeMode
:
MeasureMode
=
None
self
.
activeModeControl
:
QtWidgets
.
QGroupBox
=
QtWidgets
.
QGroupBox
()
loadDsetBtn
=
QtWidgets
.
QPushButton
(
'Load Dataset'
)
loadDsetBtn
.
released
.
connect
(
self
.
_load_dataset
)
self
.
controlGroup
=
QtWidgets
.
QGroupBox
()
self
.
controlGroupLayout
=
QtWidgets
.
QHBoxLayout
()
self
.
controlGroup
.
setLayout
(
self
.
controlGroupLayout
)
self
.
controlGroupLayout
.
addWidget
(
loadDsetBtn
)
self
.
controlGroupLayout
.
addWidget
(
QtWidgets
.
QLabel
(
'Select Subsampling Mode:'
))
self
.
controlGroupLayout
.
addWidget
(
self
.
modeSelector
)
self
.
controlGroupLayout
.
addWidget
(
self
.
activeModeControl
)
...
...
@@ -57,6 +61,12 @@ class MainView(QtWidgets.QWidget):
self
.
activeMode
.
update_measure_viewItems
()
def
_load_dataset
(
self
)
->
None
:
fname
=
QtWidgets
.
QFileDialog
.
getOpenFileName
(
self
,
'Select .pkl file'
,
filter
=
'pkl file (*.pkl)'
)
if
fname
[
0
]
!=
''
:
self
.
filterView
.
load_and_update_from_dataset
(
fname
[
0
])
self
.
activeMode
.
update_measure_viewItems
()
if
__name__
==
'__main__'
:
import
sys
...
...
gui/measureModes.py
View file @
064d26eb
...
...
@@ -24,8 +24,7 @@ class CrossBoxMode(MeasureMode):
self
.
update_measure_viewItems
()
def
update_measure_viewItems
(
self
)
->
None
:
assert
self
.
filterView
.
filter
.
height
==
self
.
filterView
.
filter
.
width
self
.
crossBoxGenerator
.
filterSize
=
self
.
filterView
.
filter
.
height
self
.
crossBoxGenerator
.
filterDiameter
=
self
.
filterView
.
filter
.
diameter
self
.
crossBoxGenerator
.
numBoxesAcross
=
int
(
self
.
uiControls
.
numBoxesSelector
.
currentText
())
desiredCoverage
:
int
=
self
.
uiControls
.
coverageSpinbox
.
value
()
...
...
@@ -36,14 +35,9 @@ class CrossBoxMode(MeasureMode):
self
.
crossBoxGenerator
.
fraction
=
desiredCoverage
/
100
viewItems
=
[]
topLefts
:
list
=
self
.
crossBoxGenerator
.
get_topLeft_of_boxes
()
boxSize
=
self
.
crossBoxGenerator
.
boxSize
for
(
x
,
y
)
in
topLefts
:
newBox
:
MeasureBoxGraphItem
=
MeasureBoxGraphItem
(
x
,
y
,
boxSize
,
boxSize
)
viewItems
.
append
(
newBox
)
self
.
filterView
.
update_measure_boxes
(
viewItems
)
self
.
filterView
.
update_measure_boxes
(
topLefts
,
boxSize
)
class
CrossBoxesControls
(
QtWidgets
.
QGroupBox
):
...
...
@@ -94,20 +88,19 @@ class SpiralBoxMode(MeasureMode):
self
.
update_measure_viewItems
()
def
update_measure_viewItems
(
self
)
->
None
:
self
.
spiralBoxGenerator
.
filterHeight
=
self
.
filterView
.
filter
.
height
self
.
spiralBoxGenerator
.
filterWidth
=
self
.
filterView
.
filter
.
width
self
.
spiralBoxGenerator
.
filterDiameter
=
self
.
filterView
.
filter
.
diameter
self
.
spiralBoxGenerator
.
boxSize
=
self
.
uiControls
.
boxSizeSpinbox
.
value
()
# minBoxSize: float = self.spiralBoxGenerator.filterDiameter*0.1
# if self.spiralBoxGenerator.boxSize < minBoxSize:
# self.spiralBoxGenerator.boxSize = minBoxSize
# self.uiControls.boxSizeSpinbox.setValue(int(round(minBoxSize)))
self
.
spiralBoxGenerator
.
numBoxes
=
self
.
uiControls
.
numBoxesSpinbox
.
value
()
viewItems
=
[]
topLefts
:
list
=
self
.
spiralBoxGenerator
.
get_topLeft_of_boxes
()
boxSize
=
self
.
spiralBoxGenerator
.
boxSize
for
(
x
,
y
)
in
topLefts
:
newBox
:
MeasureBoxGraphItem
=
MeasureBoxGraphItem
(
x
,
y
,
boxSize
,
boxSize
)
viewItems
.
append
(
newBox
)
self
.
filterView
.
update_measure_boxes
(
viewItems
)
self
.
filterView
.
update_measure_boxes
(
topLefts
,
boxSize
)
class
SpiralBoxControls
(
QtWidgets
.
QGroupBox
):
...
...
@@ -125,7 +118,7 @@ class SpiralBoxControls(QtWidgets.QGroupBox):
layout
.
addWidget
(
QtWidgets
.
QLabel
(
'Box Size:'
))
self
.
boxSizeSpinbox
=
QtWidgets
.
QSpinBox
()
self
.
boxSizeSpinbox
.
setValue
(
50
)
self
.
boxSizeSpinbox
.
setMaximum
(
1000
)
self
.
boxSizeSpinbox
.
setMaximum
(
1000
0
)
self
.
boxSizeSpinbox
.
valueChanged
.
connect
(
self
.
_config_changed
)
layout
.
addWidget
(
self
.
boxSizeSpinbox
)
...
...
helpers.py
View file @
064d26eb
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Tue Jan 28 19:32:50 2020
@author: luna
"""
from
PyQt5
import
QtCore
,
QtGui
import
numpy
as
np
import
sys
sys
.
path
.
append
(
"C://Users//xbrjos//Desktop//Python"
)
from
gepard
import
dataset
class
ParticleBinSorter
(
object
):
...
...
@@ -36,15 +32,108 @@ class ParticleBinSorter(object):
return
binIndex
def
box_contains_contour
(
boxXY
:
tuple
,
boxWidthHeight
:
tuple
,
contour
:
np
.
array
)
->
bool
:
def
box_overlaps_contour
(
boxTopLeftXY
:
tuple
,
boxWidthHeight
:
tuple
,
contour
,
offset
:
tuple
=
(
0
,
0
))
->
bool
:
"""
Calculates, if a contour is overlapping a box.
:param boxTopLeftXY: topLeft of Box
:param boxWidthHeight: Width and height of box
:param contour: np.ndarrayof contour data
:param offset: optional offset (x, y) of the box (i.e., the (0, 0) of the contours coord system does not match
the (0, 0) of the box coord system.
:return:
"""
contourPolygon
=
QtGui
.
QPolygonF
()
for
point
in
contour
:
contourPolygon
.
append
(
QtCore
.
QPointF
(
point
[
0
,
0
],
point
[
0
,
1
]))
if
type
(
contour
)
==
np
.
ndarray
:
for
point
in
contour
:
contourPolygon
.
append
(
QtCore
.
QPointF
(
point
[
0
,
0
],
point
[
0
,
1
]))
elif
type
(
contour
)
==
QtGui
.
QPolygonF
:
contourPolygon
=
contour
else
:
raise
TypeError
boxPolygon
=
QtGui
.
QPolygonF
()
boxPolygon
.
append
(
QtCore
.
QPointF
(
boxXY
[
0
],
boxXY
[
1
]))
boxPolygon
.
append
(
QtCore
.
QPointF
(
boxXY
[
0
],
boxXY
[
1
]
+
boxWidthHeight
[
1
]))
boxPolygon
.
append
(
QtCore
.
QPointF
(
boxXY
[
0
]
+
boxWidthHeight
[
0
],
boxXY
[
1
]))
boxPolygon
.
append
(
QtCore
.
QPointF
(
boxXY
[
0
]
+
boxWidthHeight
[
0
],
boxXY
[
1
]
+
boxWidthHeight
[
1
]))
boxPolygon
.
append
(
QtCore
.
QPointF
(
boxTopLeftXY
[
0
]
+
offset
[
0
],
boxTopLeftXY
[
1
]
+
offset
[
1
]))
boxPolygon
.
append
(
QtCore
.
QPointF
(
boxTopLeftXY
[
0
]
+
offset
[
0
],
boxTopLeftXY
[
1
]
+
boxWidthHeight
[
1
]
+
offset
[
1
]))
boxPolygon
.
append
(
QtCore
.
QPointF
(
boxTopLeftXY
[
0
]
+
offset
[
0
]
+
boxWidthHeight
[
0
],
boxTopLeftXY
[
1
]
+
offset
[
1
]))
boxPolygon
.
append
(
QtCore
.
QPointF
(
boxTopLeftXY
[
0
]
+
offset
[
0
]
+
boxWidthHeight
[
0
],
boxTopLeftXY
[
1
]
+
boxWidthHeight
[
1
]
+
offset
[
1
]))
isOverlapping
:
bool
=
boxPolygon
.
intersects
(
contourPolygon
)
if
not
isOverlapping
:
# sometimes, the polygon.intersects method does not capture everything... We test the brects therefore..
polygonBrect
:
QtCore
.
QRectF
=
contourPolygon
.
boundingRect
()
boxBrect
:
QtCore
.
QRectF
=
boxPolygon
.
boundingRect
()
if
boxBrect
.
contains
(
polygonBrect
)
or
boxBrect
.
intersects
(
polygonBrect
):
isOverlapping
=
True
return
isOverlapping
def
get_overlapping_fraction
(
polygon1
:
QtGui
.
QPolygonF
,
polygon2
:
QtGui
.
QPolygonF
)
->
float
:
"""
Takes two polygons and returns the overlapping fraction (in terms of area)
:param polygon1: The polygon that the fraction shall be calculated of.
:param polygon2: The overlapping polygon, which's size is not of interest
:return:
"""
overlap
:
float
=
0
overlapPoly
:
QtGui
.
QPolygonF
=
polygon1
.
intersected
(
polygon2
)
if
overlapPoly
.
size
()
>
0
:
origSize
:
float
=
get_polygon_area
(
polygon1
)
overlapSize
:
float
=
get_polygon_area
(
overlapPoly
)
overlap
=
overlapSize
/
origSize
return
overlap
def
get_polygon_area
(
polygon
:
QtGui
.
QPolygonF
)
->
float
:
"""
Calculates the area of a polygon, adapted from:
https://stackoverflow.com/questions/24467972/calculate-area-of-polygon-given-x-y-coordinates
:param polygon:
:return: area
"""
x
:
list
=
[]
y
:
list
=
[]
for
index
in
range
(
polygon
.
size
()):
point
:
QtCore
.
QPointF
=
polygon
.
at
(
index
)
x
.
append
(
point
.
x
())
y
.
append
(
point
.
y
())
x
:
np
.
ndarray
=
np
.
array
(
x
)
y
:
np
.
ndarray
=
np
.
array
(
y
)
area
=
0.5
*
np
.
abs
(
np
.
dot
(
x
,
np
.
roll
(
y
,
1
))
-
np
.
dot
(
y
,
np
.
roll
(
x
,
1
)))
return
area
def
get_filterDimensions_from_dataset
(
dataset
)
->
tuple
:
"""
Processes the datasets boundary items to calculate diameter and offset (coord system offset of circular filter
with respect to actual dataset). This is used to set circular filter dimensions to use in the geometric
subsampling approaches.
The return values are in micrometer dimensions.
:param dataset: The dataset to read.
:return: (radius, offset, widthHeight) in µm
"""
maxDim
=
dataset
.
maxdim
imgDim
=
dataset
.
imagedim_df
if
dataset
.
imagescanMode
==
'df'
else
dataset
.
imagedim_bf
minX
,
maxY
,
=
maxDim
[
0
]
-
imgDim
[
0
]
/
2
,
maxDim
[
1
]
+
imgDim
[
1
]
/
2
maxX
,
minY
=
maxDim
[
2
]
+
imgDim
[
0
]
/
2
,
maxDim
[
3
]
-
imgDim
[
1
]
/
2
width
=
maxX
-
minX
height
=
maxY
-
minY
diameter
:
float
=
min
([
width
,
height
])
offset
:
tuple
=
(
width
-
diameter
)
/
2
,
(
height
-
diameter
)
/
2
return
offset
,
diameter
,
[
width
,
height
]
return
contourPolygon
.
intersects
(
boxPolygon
)
def
convert_length_to_pixels
(
dataset
:
dataset
.
DataSet
,
length
:
float
)
->
float
:
"""
:param dataset: dataset to use for conversion
:param length: length in µm
:return: length in px
"""
imgMode
:
str
=
dataset
.
imagescanMode
pixelScale
:
float
=
(
dataset
.
pixelscale_df
if
imgMode
==
'df'
else
dataset
.
pixelscale_bf
)
length
/=
pixelScale
return
length
methods.py
View file @
064d26eb
...
...
@@ -26,30 +26,6 @@ class SubsamplingMethod(object):
raise
NotImplementedError
class
BoxSelectionSubsamplingMethod
(
SubsamplingMethod
):
def
__init__
(
self
,
*
args
):
super
(
BoxSelectionSubsamplingMethod
,
self
).
__init__
(
*
args
)
self
.
filterSize
:
float
=
500
@
property
def
filterArea
(
self
)
->
float
:
return
np
.
pi
*
(
self
.
filterSize
/
2
)
**
2
<