...
 
Commits (3)
from methods import SubsamplingMethod import numpy as np
from itertools import combinations
from methods import BoxSelectionSubsamplingMethod
from helpers import box_contains_contour
class CrossBoxSelector(SubsamplingMethod):
def __init__(self, particleContainer, desiredFraction:float = 0.1) -> None: class CrossBoxSelector(BoxSelectionSubsamplingMethod):
def __init__(self, particleContainer, desiredFraction: float = 0.1) -> None:
super(CrossBoxSelector, self).__init__(particleContainer, desiredFraction) super(CrossBoxSelector, self).__init__(particleContainer, desiredFraction)
self.fraction = desiredFraction
self.filterWidth: float = 1000 self.filterWidth: float = 1000
self.filterHeight: float = 1000 self.filterHeight: float = 1000
self.numBoxesAcross: int = 3 #either 3 or 5 self.numBoxesAcross: int = 3 # either 3 or 5
@property @property
def boxSize(self) -> float: def boxSize(self) -> float:
assert self.get_maximum_achievable_fraction() >= self.fraction maxFraction = self.get_maximum_achievable_fraction()
if maxFraction < self.fraction:
self.fraction = maxFraction
totalBoxArea: float = ((self.filterWidth*self.filterHeight)*self.fraction) totalBoxArea: float = ((self.filterWidth*self.filterHeight)*self.fraction)
boxArea: float = totalBoxArea / (2*self.numBoxesAcross - 1) boxArea: float = totalBoxArea / (2*self.numBoxesAcross - 1)
return boxArea**0.5 return boxArea**0.5
...@@ -66,4 +72,113 @@ class CrossBoxSelector(SubsamplingMethod): ...@@ -66,4 +72,113 @@ class CrossBoxSelector(SubsamplingMethod):
start: float = i * filterSize / self.numBoxesAcross + (maxBoxSize - boxSize) / 2 start: float = i * filterSize / self.numBoxesAcross + (maxBoxSize - boxSize) / 2
tileStarts.append(start) tileStarts.append(start)
return tileStarts return tileStarts
\ No newline at end of file
class SpiralSelector(BoxSelectionSubsamplingMethod):
def __init__(self, particleContainer, desiredFraction: float = 0.1) -> None:
super(SpiralSelector, self).__init__(particleContainer, desiredFraction)
self.filterWidth: float
self.filterHeight: float
self.boxSize: float = 50
self.numBoxes = 20
@property
def spiralSlope(self) -> float:
assert self.filterHeight == self.filterWidth
return self.armDistance / (2*np.pi)
@property
def armDistance(self) -> float:
return np.sqrt(2) * self.boxSize
@property
def actuallyCoveredFraction(self) -> float:
return self.numBoxes*self.boxSize**2 / (self.filterHeight*self.filterWidth)
def get_topLeft_of_boxes(self) -> list:
"""
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.filterWidth/2 - self.boxSize/2, self.filterHeight/2 - self.boxSize/2
slope = self.spiralSlope
theta: float = 0
boxDistance = self.boxSize * 1.1
topLefts: list = []
for i in range(self.numBoxes):
newPoint: tuple = self._get_xy_at_angle(theta, filterCenter)
topLefts.append(newPoint)
theta += boxDistance / (slope * np.sqrt(1 + theta**2))
boxDistance *= 1.05
topLefts = self._move_and_scale_toplefts(topLefts)
return topLefts
def _move_and_scale_toplefts(self, topLefts: list) -> list:
"""
The spiral approximation leads to boxes that are outside the filter size limits.
This function moves and scales the topLeft-Points so that all measure boxes lie within the filter limits.
:return list:
"""
assert self.filterHeight == self.filterWidth # elliptical filters are not supportet here..
xCoords: np.array = np.array([float(point[0]) for point in topLefts]) - self.filterWidth / 2
yCoords: np.array = np.array([float(point[1]) for point in topLefts]) - self.filterHeight / 2
xCoordsBoxMiddles: np.array = xCoords + self.boxSize/2
yCoordsBoxMiddles: np.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.filterHeight / 2
desiredDistanceCenter: float = desiredDistanceTotal - halfBoxDistance
scaleFactor: float = desiredDistanceCenter / distanceLastCenter
xCoordsBoxMiddles *= scaleFactor
yCoordsBoxMiddles *= scaleFactor
xCoords = xCoordsBoxMiddles + (self.filterWidth - self.boxSize)/2
yCoords = yCoordsBoxMiddles + (self.filterHeight - self.boxSize)/2
newTopLefts = zip(np.round(xCoords), np.round(yCoords))
return list(tuple(newTopLefts))
def _get_max_distance_of_boxCenter_to_center(self, boxCenter: tuple, center: tuple = (0, 0)) -> float:
"""
Calculates the maximal distance of a box to the given center
:param topLeft:
:param boxSize:
:return:
"""
center = np.array(center)
boxSize = self.boxSize
coords: np.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)
return np.max(distances)
def _get_xy_at_angle(self, theta: float, centerXY: tuple = (0, 0)) -> tuple:
distance: float = self.spiralSlope * theta
return distance * np.cos(theta) + centerXY[0], distance * np.sin(theta) + centerXY[1]
def _boxes_are_overlapping(self, topLefts: list) -> bool:
"""
Calculates if there is any overlap of the boxes
:return:
"""
overlaps: bool = False
for topLeft1, topLeft2 in combinations(topLefts, 2):
if abs(topLeft1[0] - topLeft2[0]) < self.boxSize and abs(topLeft1[1] - topLeft2[1]) < self.boxSize:
overlaps = True
break
return overlaps
...@@ -35,7 +35,7 @@ class FilterGraphItem(QtWidgets.QGraphicsItem): ...@@ -35,7 +35,7 @@ class FilterGraphItem(QtWidgets.QGraphicsItem):
""" """
The Graphical Representation of the actual filter 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):
super(FilterGraphItem, self).__init__() super(FilterGraphItem, self).__init__()
self.width = filterWidth self.width = filterWidth
self.height = filterHeight self.height = filterHeight
......
from PyQt5 import QtWidgets from PyQt5 import QtWidgets
from filterView import FilterView from gui.filterView import FilterView
from measureModes import CrossBoxMode, CrossBoxesControls from gui.measureModes import MeasureMode, CrossBoxMode, CrossBoxesControls, SpiralBoxMode
class MainView(QtWidgets.QWidget): class MainView(QtWidgets.QWidget):
...@@ -12,8 +12,9 @@ class MainView(QtWidgets.QWidget): ...@@ -12,8 +12,9 @@ class MainView(QtWidgets.QWidget):
self.setLayout(self.layout) self.setLayout(self.layout)
self.modeSelector: QtWidgets.QComboBox = QtWidgets.QComboBox() self.modeSelector: QtWidgets.QComboBox = QtWidgets.QComboBox()
self.modeSelector.currentTextChanged.connect(self._switch_to_mode)
self.measureModes: dict = {} self.measureModes: dict = {}
self.activeMode = None self.activeMode: MeasureMode = None
self.activeModeControl: QtWidgets.QGroupBox = QtWidgets.QGroupBox() self.activeModeControl: QtWidgets.QGroupBox = QtWidgets.QGroupBox()
self.controlGroup = QtWidgets.QGroupBox() self.controlGroup = QtWidgets.QGroupBox()
...@@ -31,20 +32,30 @@ class MainView(QtWidgets.QWidget): ...@@ -31,20 +32,30 @@ class MainView(QtWidgets.QWidget):
self._switch_to_default_mode() self._switch_to_default_mode()
def _add_measure_modes(self) -> None: def _add_measure_modes(self) -> None:
self.modeSelector.addItem('crossSelection') self.measureModes['spiralSelection'] = SpiralBoxMode(self.filterView)
self.measureModes['crossSelection'] = CrossBoxMode(self.filterView) self.measureModes['crossSelection'] = CrossBoxMode(self.filterView)
self.modeSelector.addItem('spiralSelection')
self.modeSelector.addItem('crossSelection')
def _switch_to_default_mode(self) -> None: def _switch_to_default_mode(self) -> None:
modes: list = list(self.measureModes.keys()) modes: list = list(self.measureModes.keys())
self._activate_mode(modes[0]) self._activate_mode(modes[0])
def _switch_to_mode(self) -> None:
newMode: str = self.modeSelector.currentText()
self._activate_mode(newMode)
def _activate_mode(self, modeName: str) -> None: def _activate_mode(self, modeName: str) -> None:
self.activeModeControl.setParent(None) requestedMode: MeasureMode = self.measureModes[modeName]
self.controlGroupLayout.removeWidget(self.activeModeControl) if requestedMode is not self._activate_mode:
self.activeModeControl.setParent(None)
self.controlGroupLayout.removeWidget(self.activeModeControl)
self.activeMode = requestedMode
self.activeModeControl = self.activeMode.get_control_groupBox()
self.controlGroupLayout.insertWidget(2, self.activeModeControl)
self.activeMode = self.measureModes[modeName] self.activeMode.update_measure_viewItems()
self.activeModeControl = self.activeMode.get_control_groupBox()
self.controlGroupLayout.insertWidget(2, self.activeModeControl)
if __name__ == '__main__': if __name__ == '__main__':
......
from PyQt5 import QtCore, QtWidgets from PyQt5 import QtCore, QtWidgets
# import os, sys, inspect from gui.filterView import FilterView, MeasureBoxGraphItem
# currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) from geometricMethods import CrossBoxSelector, SpiralSelector
# parentdir = os.path.dirname(currentdir)
# sys.path.insert(0, parentdir)
# del currentdir, parentdir
from filterView import FilterView, MeasureBoxGraphItem
from geometricOperations import CrossBoxSelector
class MeasureMode(QtCore.QObject): class MeasureMode(QtCore.QObject):
...@@ -88,4 +83,59 @@ class CrossBoxesControls(QtWidgets.QGroupBox): ...@@ -88,4 +83,59 @@ class CrossBoxesControls(QtWidgets.QGroupBox):
if maxCoverage < self.coverageSpinbox.value(): if maxCoverage < self.coverageSpinbox.value():
self.coverageSpinbox.valueChanged.disconnect() self.coverageSpinbox.valueChanged.disconnect()
self.coverageSpinbox.setValue(maxCoverage) self.coverageSpinbox.setValue(maxCoverage)
self.coverageSpinbox.valueChanged.connect(self._config_changed) self.coverageSpinbox.valueChanged.connect(self._config_changed)
\ No newline at end of file
class SpiralBoxMode(MeasureMode):
def __init__(self, *args):
super(SpiralBoxMode, self).__init__(*args)
self.uiControls: SpiralBoxControls = SpiralBoxControls(self)
self.spiralBoxGenerator: SpiralSelector = SpiralSelector(None)
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.boxSize = self.uiControls.boxSizeSpinbox.value()
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)
class SpiralBoxControls(QtWidgets.QGroupBox):
"""
Gives a groupbox with the controls for setting up the cross boxes.
"""
def __init__(self, measureModeParent: MeasureMode):
super(SpiralBoxControls, self).__init__()
self.setTitle('Spiral Box Controls')
self.measureModeParent = measureModeParent
layout = QtWidgets.QHBoxLayout()
self.setLayout(layout)
layout.addWidget(QtWidgets.QLabel('Box Size:'))
self.boxSizeSpinbox = QtWidgets.QSpinBox()
self.boxSizeSpinbox.setValue(50)
self.boxSizeSpinbox.setMaximum(1000)
self.boxSizeSpinbox.valueChanged.connect(self._config_changed)
layout.addWidget(self.boxSizeSpinbox)
layout.addStretch()
layout.addWidget(QtWidgets.QLabel('Num Boxes:'))
self.numBoxesSpinbox = QtWidgets.QSpinBox()
self.numBoxesSpinbox.setValue(10)
self.numBoxesSpinbox.valueChanged.connect(self._config_changed)
layout.addWidget(self.numBoxesSpinbox)
def _config_changed(self):
self.measureModeParent.update_measure_viewItems()
...@@ -5,6 +5,9 @@ Created on Tue Jan 28 19:32:50 2020 ...@@ -5,6 +5,9 @@ Created on Tue Jan 28 19:32:50 2020
@author: luna @author: luna
""" """
from PyQt5 import QtCore, QtGui
import numpy as np
class ParticleBinSorter(object): class ParticleBinSorter(object):
def __init__(self): def __init__(self):
...@@ -20,7 +23,7 @@ class ParticleBinSorter(object): ...@@ -20,7 +23,7 @@ class ParticleBinSorter(object):
return particlesInBins return particlesInBins
def _get_empty_bins(self): def _get_empty_bins(self):
return [[] for i in range(len(self.bins)+1)] return [[] for _ in range(len(self.bins)+1)]
def _get_binIndex_of_particle(self, particle): def _get_binIndex_of_particle(self, particle):
size = particle.getParticleSize() size = particle.getParticleSize()
...@@ -30,4 +33,18 @@ class ParticleBinSorter(object): ...@@ -30,4 +33,18 @@ class ParticleBinSorter(object):
break break
else: else:
binIndex += 1 binIndex += 1
return binIndex return binIndex
\ No newline at end of file
def box_contains_contour(boxXY: tuple, boxWidthHeight: tuple, contour: np.array) -> bool:
contourPolygon = QtGui.QPolygonF()
for point in contour:
contourPolygon.append(QtCore.QPointF(point[0, 0], point[0, 1]))
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]))
return contourPolygon.intersects(boxPolygon)
...@@ -9,21 +9,46 @@ import random ...@@ -9,21 +9,46 @@ import random
import numpy as np import numpy as np
from helpers import ParticleBinSorter from helpers import ParticleBinSorter
class SubsamplingMethod(object): class SubsamplingMethod(object):
def __init__(self, particleConatainer, desiredFraction: float = 0.2): def __init__(self, particleConatainer, desiredFraction: float = 0.2):
super(SubsamplingMethod, self).__init__() super(SubsamplingMethod, self).__init__()
self.particleContainer = particleConatainer self.particleContainer = particleConatainer
self.fraction = desiredFraction self.fraction = desiredFraction
def apply_subsampling_method(self): def apply_subsampling_method(self) -> tuple:
"""
Takes all particles from the supplied particle conatiner and returns a new list of particles that
were measured by applying that subsampling procedure. Also, the actualy measured fraction is returned.
(The desired fraction may not always be achievable)
:returns actuallyMeasuredFraction, listOfSubParticles:
"""
raise NotImplementedError raise NotImplementedError
class RandomSampling(SubsamplingMethod): class BoxSelectionSubsamplingMethod(SubsamplingMethod):
#def __init__(self, particleContainer, desiredFraction=0.1): def __init__(self, *args):
#super(RandomSampling, self).__init__(particleContainer) super(BoxSelectionSubsamplingMethod, self).__init__(*args)
#self.fraction = desiredFraction
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_contains_contour(topLeft, (boxSize, boxSize), particle.contour):
subParticles.append(particle)
return self.fraction, subParticles
def get_topLeft_of_boxes(self) -> list:
raise NotImplementedError
def get_parameterCombos_for_fraction(self) -> list:
raise NotImplementedError
class RandomSampling(SubsamplingMethod):
def apply_subsampling_method(self): def apply_subsampling_method(self):
numOrigParticles = len(self.particleContainer.particles) numOrigParticles = len(self.particleContainer.particles)
numParticles = self._get_number_of_random_particles(numOrigParticles) numParticles = self._get_number_of_random_particles(numOrigParticles)
...@@ -73,7 +98,7 @@ class SizeBinFractioning(SubsamplingMethod): ...@@ -73,7 +98,7 @@ class SizeBinFractioning(SubsamplingMethod):
subParticlesPerBin: list = [] subParticlesPerBin: list = []
for particles in particlesInBins: for particles in particlesInBins:
numParticlesInBin: int = len(particles) numParticlesInBin: int = len(particles)
numSubParticlesPerBin:int = np.int(np.round(numParticlesInBin * self.fraction)) numSubParticlesPerBin: int = np.int(np.round(numParticlesInBin * self.fraction))
if numSubParticlesPerBin == 0 and numParticlesInBin > 0: if numSubParticlesPerBin == 0 and numParticlesInBin > 0:
numSubParticlesPerBin = 1 numSubParticlesPerBin = 1
...@@ -81,10 +106,3 @@ class SizeBinFractioning(SubsamplingMethod): ...@@ -81,10 +106,3 @@ class SizeBinFractioning(SubsamplingMethod):
subParticlesPerBin.append(subParticlesInBin) subParticlesPerBin.append(subParticlesInBin)
return subParticlesPerBin return subParticlesPerBin
class SelectSquaresFromFilter(SubsamplingMethod):
def __init__(self, particleContainer, desiredFraction):
super(SelectSquaresFromFilter, self).__init__(particleContainer)
self.fraction = desiredFraction
...@@ -45,7 +45,7 @@ for fraction in fractions: ...@@ -45,7 +45,7 @@ for fraction in fractions:
iterErrors.append(resultComparer._get_mp_count_error(origParticles, randomParticles, randomFraction)) iterErrors.append(resultComparer._get_mp_count_error(origParticles, randomParticles, randomFraction))
bins, errorsPerBin = resultComparer._get_mp_count_error_per_bin(origParticles, randomParticles, randomFraction) bins, errorsPerBin = resultComparer._get_mp_count_error_per_bin(origParticles, randomParticles, randomFraction)
binIterErrors.append(errorsPerBin) binIterErrors.append(errorsPerBin)
errors.append(round(np.mean(iterErrors)*100)) #from fraction to % errors.append(round(np.mean(iterErrors)*100)) #from fraction to %
fractionBinErrors = [] fractionBinErrors = []
for binIndex in range(len(bins)+1): for binIndex in range(len(bins)+1):
......
import unittest import unittest
from geometricOperations import CrossBoxSelector import numpy as np
from geometricMethods import CrossBoxSelector, SpiralSelector
class TestSelectBoxes(unittest.TestCase):
class TestSelectCrossBoxes(unittest.TestCase):
def setUp(self) -> None: def setUp(self) -> None:
self.crossBoxSelector = CrossBoxSelector(None) self.crossBoxSelector = CrossBoxSelector(None)
...@@ -10,7 +12,7 @@ class TestSelectBoxes(unittest.TestCase): ...@@ -10,7 +12,7 @@ class TestSelectBoxes(unittest.TestCase):
self.crossBoxSelector.fraction = 0.1 self.crossBoxSelector.fraction = 0.1
self.crossBoxSelector.numBoxesAcross = 3 self.crossBoxSelector.numBoxesAcross = 3
topLeftCorners:list = self.crossBoxSelector.get_topLeft_of_boxes() topLeftCorners: list = self.crossBoxSelector.get_topLeft_of_boxes()
self.assertEqual(len(topLeftCorners), 5) self.assertEqual(len(topLeftCorners), 5)
self.crossBoxSelector.numBoxesAcross = 5 self.crossBoxSelector.numBoxesAcross = 5
...@@ -44,7 +46,8 @@ class TestSelectBoxes(unittest.TestCase): ...@@ -44,7 +46,8 @@ class TestSelectBoxes(unittest.TestCase):
horizontalTileStarts: list = self.crossBoxSelector._get_horizontal_box_starts(halfBoxSize) horizontalTileStarts: list = self.crossBoxSelector._get_horizontal_box_starts(halfBoxSize)
verticalTileStarts: list = self.crossBoxSelector._get_vertical_box_starts(halfBoxSize) verticalTileStarts: list = self.crossBoxSelector._get_vertical_box_starts(halfBoxSize)
expectedOffset = (maxBoxSize - halfBoxSize) / 2 expectedOffset = (maxBoxSize - halfBoxSize) / 2
self.assertEqual(horizontalTileStarts, [0+expectedOffset, 100/5+expectedOffset, 2*100/5+expectedOffset, 3*100/5+expectedOffset, 4*100/5+expectedOffset]) self.assertEqual(horizontalTileStarts, [0+expectedOffset, 100/5+expectedOffset, 2*100/5+expectedOffset,
3*100/5+expectedOffset, 4*100/5+expectedOffset])
self.assertEqual(horizontalTileStarts, verticalTileStarts) self.assertEqual(horizontalTileStarts, verticalTileStarts)
def test_get_box_size(self) -> None: def test_get_box_size(self) -> None:
...@@ -59,4 +62,59 @@ class TestSelectBoxes(unittest.TestCase): ...@@ -59,4 +62,59 @@ class TestSelectBoxes(unittest.TestCase):
areaPerBox: float = totalBoxArea / numBoxes areaPerBox: float = totalBoxArea / numBoxes
boxSize: float = areaPerBox**0.5 boxSize: float = areaPerBox**0.5
self.assertEqual(self.crossBoxSelector.boxSize, boxSize) self.assertEqual(self.crossBoxSelector.boxSize, boxSize)
\ No newline at end of file
class TestSelectSpiralBoxes(unittest.TestCase):
def setUp(self) -> None:
self.spiralBoxSelector: SpiralSelector = SpiralSelector(None)
def test_move_and_scale_toplefts(self):
self.spiralBoxSelector.filterHeight = 100
self.spiralBoxSelector.filterWidth = 100
self.spiralBoxSelector.boxSize = 10
topLefts = [(45, 45), (0, 45), (90, 45)]
newTopLefts = self.spiralBoxSelector._move_and_scale_toplefts(topLefts)
self.assertEqual(newTopLefts[0], (45, 45))
self.assertEqual(newTopLefts[1], (0, 45))
self.assertEqual(newTopLefts[2], (90, 45))
def test_get_max_distance_of_boxCenter_to_center(self):
boxCenter = 0, 0
self.spiralBoxSelector.boxSize = 1
maxDistance: float = self.spiralBoxSelector._get_max_distance_of_boxCenter_to_center(boxCenter)
self.assertEqual(maxDistance, np.sqrt(0.5**2 + 0.5**2))
self.spiralBoxSelector.boxSize = 2
maxDistance: float = self.spiralBoxSelector._get_max_distance_of_boxCenter_to_center(boxCenter)
self.assertEqual(maxDistance, np.sqrt(2))
boxCenter = 1, 0
self.spiralBoxSelector.boxSize = 2
maxDistance: float = self.spiralBoxSelector._get_max_distance_of_boxCenter_to_center(boxCenter)
self.assertEqual(maxDistance, np.sqrt(2**2 + 1**2))
boxCenter = -1, -3
self.spiralBoxSelector.boxSize = 2
maxDistance: float = self.spiralBoxSelector._get_max_distance_of_boxCenter_to_center(boxCenter)
self.assertEqual(maxDistance, np.sqrt(4**2 + 2**2))
def test_boxes_are_overlapping(self):
self.spiralBoxSelector.boxSize = 10
topLefts: list = [(0, 0), (10, 10), (20, 10)]
overlaps: bool = self.spiralBoxSelector._boxes_are_overlapping(topLefts)
self.assertEqual(overlaps, False)
topLefts: list = [(0, 0), (9, 10)]
overlaps: bool = self.spiralBoxSelector._boxes_are_overlapping(topLefts)
self.assertEqual(overlaps, False)
topLefts: list = [(0, 0), (9, 9)]
overlaps: bool = self.spiralBoxSelector._boxes_are_overlapping(topLefts)
self.assertEqual(overlaps, True)
topLefts: list = [(-2, 0), (5, 9)]
overlaps: bool = self.spiralBoxSelector._boxes_are_overlapping(topLefts)
self.assertEqual(overlaps, True)
import unittest import unittest
from pytestqt import qtbot
from PyQt5.QtWidgets import QApplication from PyQt5.QtWidgets import QApplication
import sys import sys
from gui.mainView import MainView
from gui.measureModes import CrossBoxesControls
app = QApplication(sys.argv) app = QApplication(sys.argv)
class TestFilterViewGUI(unittest.TestCase): def test_hello(qtbot):
def setUp(self) -> None: widget = MainView()
pass qtbot.addWidget(widget)
print('nu')
def test_gui(self): #
#NEEDS TO BE IMPLEMENTED, too much was refactored to make the old test pass... # class TestMainViewGUI(unittest.TestCase):
self.assertTrue(False) # def setUp(self) -> None:
\ No newline at end of file # # self.qbot = qtbot.QtBot(None)
# self.mainView: MainView = MainView()
# # self.qbot.addWidget(self.mainView)
#
# def test_switchModes_modes(self, qtbot = qtbot.QtBot):
# self.mainView._activate_mode('crossSelection')
# controls = self.mainView.activeModeControl
# self.assertTrue(type(controls) == CrossBoxesControls)
#
# numBoxComboBox = controls.numBoxesSelector
# qtbot.addWidget(self.mainView)
\ No newline at end of file
...@@ -7,12 +7,14 @@ Created on Tue Jan 28 19:35:04 2020 ...@@ -7,12 +7,14 @@ Created on Tue Jan 28 19:35:04 2020
""" """
import unittest import unittest
from helpers import ParticleBinSorter from helpers import ParticleBinSorter, box_contains_contour
import numpy as np
import sys import sys
sys.path.append("C://Users//xbrjos//Desktop//Python") sys.path.append("C://Users//xbrjos//Desktop//Python")
import gepard import gepard
from gepard.analysis.particleAndMeasurement import Particle from gepard.analysis.particleAndMeasurement import Particle
class TestBinSorter(unittest.TestCase): class TestBinSorter(unittest.TestCase):
def setUp(self): def setUp(self):
self.sorter = ParticleBinSorter() self.sorter = ParticleBinSorter()
...@@ -20,10 +22,13 @@ class TestBinSorter(unittest.TestCase): ...@@ -20,10 +22,13 @@ class TestBinSorter(unittest.TestCase):
def test_sort_particles_into_bins(self): def test_sort_particles_into_bins(self):
particleList = [] particleList = []
upperBinLimit = None
for upperBinLimit in self.bins: for upperBinLimit in self.bins:
newParticle = Particle() newParticle = Particle()
newParticle.longSize = newParticle.shortSize = upperBinLimit - 1 newParticle.longSize = newParticle.shortSize = upperBinLimit - 1
particleList.append(newParticle) particleList.append(newParticle)
lastParticle = Particle() lastParticle = Particle()
lastParticle.longSize = lastParticle.shortSize = upperBinLimit + 1 lastParticle.longSize = lastParticle.shortSize = upperBinLimit + 1
particleList.append(lastParticle) particleList.append(lastParticle)
...@@ -58,7 +63,18 @@ class TestBinSorter(unittest.TestCase): ...@@ -58,7 +63,18 @@ class TestBinSorter(unittest.TestCase):
particle.longSize = particle.shortSize = 1000 particle.longSize = particle.shortSize = 1000
binIndex = self.sorter._get_binIndex_of_particle(particle) binIndex = self.sorter._get_binIndex_of_particle(particle)
self.assertEqual(binIndex, 7) self.assertEqual(binIndex, 7)
if __name__ == '__main__': class TestOther(unittest.TestCase):
unittest.main() def test_box_contains_contour(self):
\ No newline at end of file boxXY: tuple = 0, 0
boxWidthHeight: tuple = 10, 10
contourPoints = np.array([[[0, 0]], [[5, 5]], [[3, 3]]]) # fully enclosed
self.assertTrue(box_contains_contour(boxXY, boxWidthHeight, contourPoints))
contourPoints = np.array([[[-5, -5]], [[0, 5]], [[-5, -10]]]) # only one point touches border
self.assertTrue(box_contains_contour(boxXY, boxWidthHeight, contourPoints))
contourPoints = np.array([[[-5, -5]], [[-1, 5]], [[-5, -10]]]) # outside the box
self.assertFalse(box_contains_contour(boxXY, boxWidthHeight, contourPoints))
...@@ -16,11 +16,13 @@ from gepard.analysis.particleAndMeasurement import Particle ...@@ -16,11 +16,13 @@ from gepard.analysis.particleAndMeasurement import Particle
from methods import IvlevaSubsampling, RandomSampling, SizeBinFractioning from methods import IvlevaSubsampling, RandomSampling, SizeBinFractioning
from helpers import ParticleBinSorter from helpers import ParticleBinSorter
def get_default_particle_container(numParticles=1000): def get_default_particle_container(numParticles=1000):
particleContainer = ParticleContainer(None) particleContainer = ParticleContainer(None)
particleContainer.initializeParticles(numParticles) particleContainer.initializeParticles(numParticles)
return particleContainer return particleContainer
class TestIvleva(unittest.TestCase): class TestIvleva(unittest.TestCase):
def test_get_ivleva_fraction(self): def test_get_ivleva_fraction(self):
self.particleContainer = get_default_particle_container() self.particleContainer = get_default_particle_container()
...@@ -62,7 +64,7 @@ class TestSizeBinFractioning(unittest.TestCase): ...@@ -62,7 +64,7 @@ class TestSizeBinFractioning(unittest.TestCase):
self.sizeBinFrac = SizeBinFractioning(None) self.sizeBinFrac = SizeBinFractioning(None)
sorter = ParticleBinSorter() sorter = ParticleBinSorter()
sizes = [limit-1 for limit in sorter.bins] sizes = [limit-1 for limit in sorter.bins]
sizes.append(sorter.bins[-1]+1) #the last bin, that goes until infinity sizes.append(sorter.bins[-1]+1) # the last bin, that goes until infinity
self.numMPparticlesPerBin = 10 self.numMPparticlesPerBin = 10
self.particles = [] self.particles = []
...@@ -84,10 +86,6 @@ class TestSizeBinFractioning(unittest.TestCase): ...@@ -84,10 +86,6 @@ class TestSizeBinFractioning(unittest.TestCase):
for subParticles in subParticlesPerBin: for subParticles in subParticlesPerBin:
self.assertEqual(len(subParticles), numParticlesPerBinExpected) self.assertEqual(len(subParticles), numParticlesPerBinExpected)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()
\ No newline at end of file