...
 
Commits (3)
...@@ -13,6 +13,7 @@ import concurrent.futures ...@@ -13,6 +13,7 @@ import concurrent.futures
import sys import sys
sys.path.append("C://Users//xbrjos//Desktop//Python") sys.path.append("C://Users//xbrjos//Desktop//Python")
from gepard import dataset from gepard import dataset
from gepard.analysis.particleAndMeasurement import Particle
from helpers import ParticleBinSorter from helpers import ParticleBinSorter
import methods as meth import methods as meth
...@@ -42,7 +43,8 @@ def get_methods_to_test(dataset: dataset.DataSet, fractions: list = []) -> list: ...@@ -42,7 +43,8 @@ def get_methods_to_test(dataset: dataset.DataSet, fractions: list = []) -> list:
boxCreator: gmeth.BoxSelectionCreator = gmeth.BoxSelectionCreator(dataset) boxCreator: gmeth.BoxSelectionCreator = gmeth.BoxSelectionCreator(dataset)
methods += boxCreator.get_crossBoxSubsamplers_for_fraction(fraction) methods += boxCreator.get_crossBoxSubsamplers_for_fraction(fraction)
methods += boxCreator.get_spiralBoxSubsamplers_for_fraction(fraction) methods += boxCreator.get_spiralBoxSubsamplers_for_fraction(fraction)
methods.append(cmeth.ChemometricSubsampling(particleContainer, fraction)) methods += boxCreator.get_randomBoxSubsamplers_for_fraction(fraction)
# methods.append(cmeth.ChemometricSubsampling(particleContainer, fraction))
return methods return methods
...@@ -53,6 +55,17 @@ def update_sample(sample, force: bool, index: int): ...@@ -53,6 +55,17 @@ def update_sample(sample, force: bool, index: int):
sample.update_result_with_methods(methods, force) sample.update_result_with_methods(methods, force)
return sample, index return sample, index
def is_MP_particle(particle: Particle) -> bool:
# TODO: UPDATE PATTERNS -> ARE THESE REASONABLE???
isMP: bool = False
mpPatterns = ['poly', 'rubber', 'pb', 'pr', 'pg', 'py', 'pv']
assignment = particle.getParticleAssignment()
for pattern in mpPatterns:
if assignment.lower().find(pattern) != -1:
isMP = True
break
return isMP
class TotalResults(object): class TotalResults(object):
def __init__(self): def __init__(self):
...@@ -85,7 +98,8 @@ class TotalResults(object): ...@@ -85,7 +98,8 @@ class TotalResults(object):
indices: list = list(np.arange(len(self.sampleResults))) indices: list = list(np.arange(len(self.sampleResults)))
numSamples: int = len(forceList) numSamples: int = len(forceList)
numWorkers: int = 4 # in case of quadcore processor that seams reasonable?? numWorkers: int = 4 # in case of quadcore processor that seams reasonable??
chunksize: int = numSamples // numWorkers chunksize: int = int(round(numSamples / numWorkers * 0.7)) # we want to have slightly more chunks than workers
print(f'multiprocessing with {numSamples} samples and chunksize of {chunksize}')
with concurrent.futures.ProcessPoolExecutor() as executor: with concurrent.futures.ProcessPoolExecutor() as executor:
results = executor.map(update_sample, self.sampleResults, forceList, indices, chunksize=chunksize) results = executor.map(update_sample, self.sampleResults, forceList, indices, chunksize=chunksize)
...@@ -137,15 +151,17 @@ class SubsamplingResult(object): ...@@ -137,15 +151,17 @@ class SubsamplingResult(object):
""" """
Stores all interesting results from a subsampling experiment Stores all interesting results from a subsampling experiment
""" """
# TODO: UPDATE PATTERNS -> ARE THESE REASONABLE??? # # # TODO: UPDATE PATTERNS -> ARE THESE REASONABLE???
mpPatterns = ['poly', 'rubber', 'pb', 'pr', 'pg', 'py', 'pv'] # mpPatterns = ['poly', 'rubber', 'pb', 'pr', 'pg', 'py', 'pv']
def __init__(self, subsamplingMethod: meth.SubsamplingMethod): def __init__(self, subsamplingMethod: meth.SubsamplingMethod):
super(SubsamplingResult, self).__init__() super(SubsamplingResult, self).__init__()
self.method: meth.SubsamplingMethod = subsamplingMethod self.method: meth.SubsamplingMethod = subsamplingMethod
self.mpCountErrors: list = [] self.mpCountErrors: list = []
# self.origParticleCount: int = None self.origParticleCount: int = 0
# self.subSampledParticleCount: int = None self.subSampledParticleCount: int = 0
self.origMPCount: int = 0
self.estimMPCounts: list = []
# self.mpCountErrorPerBin: tuple = None # self.mpCountErrorPerBin: tuple = None
@property @property
...@@ -159,15 +175,20 @@ class SubsamplingResult(object): ...@@ -159,15 +175,20 @@ class SubsamplingResult(object):
def mpCountErrorStDev(self) -> float: def mpCountErrorStDev(self) -> float:
stdev: float = 0.0 stdev: float = 0.0
if len(self.mpCountErrors) > 0: if len(self.mpCountErrors) > 0:
stdev = np.std(self.mpCountErrors) stdev = float(np.std(self.mpCountErrors))
return stdev return stdev
@property
def estimMPCount(self) -> float:
return float(np.mean(self.estimMPCounts))
def reset_results(self) -> None: def reset_results(self) -> None:
""" """
Deletes all results Deletes all results
:return: :return:
""" """
self.mpCountErrors = [] self.mpCountErrors = []
self.estimMPCounts = []
def add_result(self, origParticles: list, subParticles: list) -> None: def add_result(self, origParticles: list, subParticles: list) -> None:
""" """
...@@ -181,6 +202,7 @@ class SubsamplingResult(object): ...@@ -181,6 +202,7 @@ class SubsamplingResult(object):
# error: float = self._get_mp_count_error(origParticles, subParticles, 1.0) # error: float = self._get_mp_count_error(origParticles, subParticles, 1.0)
# else: # else:
error: float = self._get_mp_count_error(origParticles, subParticles, self.method.fraction) error: float = self._get_mp_count_error(origParticles, subParticles, self.method.fraction)
self.origParticleCount = len(origParticles)
self.mpCountErrors.append(error) self.mpCountErrors.append(error)
def _get_mp_count_error_per_bin(self, allParticles: list, subParticles: list, fractionMeasured: float) -> tuple: def _get_mp_count_error_per_bin(self, allParticles: list, subParticles: list, fractionMeasured: float) -> tuple:
...@@ -194,7 +216,9 @@ class SubsamplingResult(object): ...@@ -194,7 +216,9 @@ class SubsamplingResult(object):
def _get_mp_count_error(self, allParticles: list, subParticles: list, fractionMeasured: float) -> float: def _get_mp_count_error(self, allParticles: list, subParticles: list, fractionMeasured: float) -> float:
numMPOrig = self._get_number_of_MP_particles(allParticles) numMPOrig = self._get_number_of_MP_particles(allParticles)
self.origMPCount = numMPOrig
numMPEstimate = self._get_number_of_MP_particles(subParticles) / fractionMeasured numMPEstimate = self._get_number_of_MP_particles(subParticles) / fractionMeasured
self.estimMPCounts.append(numMPEstimate)
if numMPOrig != 0: if numMPOrig != 0:
mpCountError = self._get_error_from_values(numMPOrig, numMPEstimate) mpCountError = self._get_error_from_values(numMPOrig, numMPEstimate)
...@@ -212,12 +236,8 @@ class SubsamplingResult(object): ...@@ -212,12 +236,8 @@ class SubsamplingResult(object):
def _get_number_of_MP_particles(self, particleList: list) -> int: def _get_number_of_MP_particles(self, particleList: list) -> int:
numMPParticles = 0 numMPParticles = 0
for particle in particleList: for particle in particleList:
assignment = particle.getParticleAssignment() if is_MP_particle(particle):
for pattern in self.mpPatterns: numMPParticles += 1
if assignment.lower().find(pattern) != -1:
numMPParticles += 1
break
return numMPParticles return numMPParticles
......
This diff is collapsed.
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
import numpy as np
from evaluation import TotalResults
def get_error_vs_frac_plot(totalResults: TotalResults, attributes: list = [], methods: list = []) -> Figure:
assert len(attributes) == len(methods)
fig: Figure = plt.figure()
numRows: int = 1
numCols: int = 1
if len(attributes) == 0:
attributes = methods = [[]]
elif len(attributes) <= 2:
numCols = len(attributes)
else:
numRows = 2
numCols = np.ceil(len(attributes)/numRows)
index = 0
for attrs, meths in zip(attributes, methods):
ax = fig.add_subplot(numRows, numCols, index + 1)
errorPerFraction: dict = totalResults.get_error_vs_fraction_data(attributes=attrs,
methods=meths)
for methodLabel in errorPerFraction.keys():
errorDict: dict = errorPerFraction[methodLabel]
fractions: list = list(errorDict.keys())
errors: list = [errorDict[fraction][0] for fraction in fractions]
stdevs: list = [errorDict[fraction][1] for fraction in fractions]
ax.errorbar(fractions, errors, stdevs, label=methodLabel, marker='s', capsize=5)
title: str = ''
if len(attrs) > 0:
title = ', '.join(attr for attr in attrs)
print('title is', title)
ax.set_title(title, fontSize=15)
ax.set_xscale('log')
ax.set_xlabel('measured fraction', fontsize=12)
ax.set_ylabel('mpCountError (%)', fontsize=12)
ax.set_xlim([0.9 * min(fractions), 1.05])
ax.set_ylim([0, 100])
ax.legend()
index += 1
return fig
# def get_grouped_spectra_plot(groupedSpectra: list, wavenumbers=None) -> matplotlib.figure.Figure:
# if wavenumbers is None:
# wavenumbers = np.arange(len(groupedSpectra[0][0]))
#
# numLabels = len(groupedSpectra)
# numRows = numLabels // 3
# numCols = np.ceil(numLabels / numRows)
# fig: matplotlib.figure.Figure = plt.figure()
#
# for index, specs in enumerate(groupedSpectra):
# ax = fig.add_subplot(numRows, numCols, index + 1)
# for spec in specs:
# ax.plot(wavenumbers, spec)
# ax.set_title(f'{len(specs)} spectra of label {index + 1}')
#
# return fig
\ No newline at end of file
...@@ -6,7 +6,7 @@ import gepard ...@@ -6,7 +6,7 @@ import gepard
from gepard import dataset from gepard import dataset
import helpers import helpers
from cythonModules import rotateContour as rc from cythonModules import rotateContour as rc
from evaluation import is_MP_particle
class FilterView(QtWidgets.QGraphicsView): class FilterView(QtWidgets.QGraphicsView):
...@@ -65,7 +65,7 @@ class FilterView(QtWidgets.QGraphicsView): ...@@ -65,7 +65,7 @@ class FilterView(QtWidgets.QGraphicsView):
self._remove_particle_contours() self._remove_particle_contours()
if self.dataset is not None: if self.dataset is not None:
for particle in self.dataset.particleContainer.particles: for particle in self.dataset.particleContainer.particles:
newContour: ParticleContour = ParticleContour(particle.contour) newContour: ParticleContour = ParticleContour(particle.contour, is_MP_particle(particle))
self.scene().addItem(newContour) self.scene().addItem(newContour)
self.contourItems.append(newContour) self.contourItems.append(newContour)
...@@ -167,12 +167,13 @@ class MeasureBoxGraphItem(QtWidgets.QGraphicsItem): ...@@ -167,12 +167,13 @@ class MeasureBoxGraphItem(QtWidgets.QGraphicsItem):
class ParticleContour(QtWidgets.QGraphicsItem): class ParticleContour(QtWidgets.QGraphicsItem):
def __init__(self, contourData, pos=(0, 0)) -> None: def __init__(self, contourData, isMP: bool = False, pos: tuple = (0, 0)) -> None:
super(ParticleContour, self).__init__() super(ParticleContour, self).__init__()
self.setZValue(1) self.setZValue(1)
self.setPos(pos[0], pos[1]) self.setPos(pos[0], pos[1])
self.brect: QtCore.QRectF = QtCore.QRectF(0, 0, 1, 1) self.brect: QtCore.QRectF = QtCore.QRectF(0, 0, 1, 1)
self.isMP: bool = isMP
self.isMeasured: bool = False # Wether the particle overlaps with a measuring box or nt self.isMeasured: bool = False # Wether the particle overlaps with a measuring box or nt
self.contourData = contourData self.contourData = contourData
self.polygon = None self.polygon = None
...@@ -199,11 +200,13 @@ class ParticleContour(QtWidgets.QGraphicsItem): ...@@ -199,11 +200,13 @@ class ParticleContour(QtWidgets.QGraphicsItem):
def paint(self, painter, option, widget) -> None: def paint(self, painter, option, widget) -> None:
if self.polygon is not None: if self.polygon is not None:
if self.isMeasured: if self.isMP:
painter.setPen(QtCore.Qt.darkRed) painter.setPen(QtCore.Qt.darkRed)
painter.setBrush(QtCore.Qt.red) painter.setBrush(QtCore.Qt.red)
else: else:
painter.setPen(QtCore.Qt.darkCyan) painter.setPen(QtCore.Qt.darkBlue)
painter.setBrush(QtCore.Qt.cyan) painter.setBrush(QtCore.Qt.blue)
painter.setOpacity(1 if self.isMeasured else 0.2)
painter.drawPolygon(self.polygon) painter.drawPolygon(self.polygon)
from PyQt5 import QtWidgets from PyQt5 import QtWidgets, QtCore
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 import dataset from gepard import dataset
from gui.filterView import FilterView from gui.filterView import FilterView
from gui.measureModes import MeasureMode, CrossBoxMode, CrossBoxesControls, SpiralBoxMode from gui.measureModes import *
import helpers import helpers
from evaluation import SubsamplingResult
class MainView(QtWidgets.QWidget): class MainView(QtWidgets.QWidget):
...@@ -30,7 +31,7 @@ class MainView(QtWidgets.QWidget): ...@@ -30,7 +31,7 @@ class MainView(QtWidgets.QWidget):
self.rotationSpinBox.setMaximum(359) self.rotationSpinBox.setMaximum(359)
self.rotationSpinBox.setValue(0) self.rotationSpinBox.setValue(0)
self.rotationSpinBox.setMaximumWidth(50) self.rotationSpinBox.setMaximumWidth(50)
self.rotationSpinBox.valueChanged.connect(self._update_fiter_rotation) self.rotationSpinBox.valueChanged.connect(self._update_filter_rotation)
self.controlGroup = QtWidgets.QGroupBox() self.controlGroup = QtWidgets.QGroupBox()
self.controlGroupLayout = QtWidgets.QHBoxLayout() self.controlGroupLayout = QtWidgets.QHBoxLayout()
...@@ -44,17 +45,27 @@ class MainView(QtWidgets.QWidget): ...@@ -44,17 +45,27 @@ class MainView(QtWidgets.QWidget):
self.controlGroupLayout.addWidget(self.activeModeControl) self.controlGroupLayout.addWidget(self.activeModeControl)
self.controlGroupLayout.addStretch() self.controlGroupLayout.addStretch()
self.infoWidget: SampleInfoWidget = SampleInfoWidget()
self.layout.addWidget(self.controlGroup) self.layout.addWidget(self.controlGroup)
self.layout.addWidget(self.infoWidget)
self.filterView = FilterView() self.filterView = FilterView()
self.layout.addWidget(self.filterView) self.layout.addWidget(self.filterView)
self._add_measure_modes() self._add_measure_modes()
self._switch_to_default_mode() self._switch_to_default_mode()
def _add_measure_modes(self) -> None: def _add_measure_modes(self) -> None:
self.measureModes['spiralSelection'] = SpiralBoxMode(self.filterView) self.measureModes['Spiral Box Selection'] = SpiralBoxMode(self.filterView)
self.measureModes['crossSelection'] = CrossBoxMode(self.filterView) self.measureModes['Cross Box Selection'] = CrossBoxMode(self.filterView)
self.modeSelector.addItem('spiralSelection') self.measureModes['Random Box Selection'] = RandomBoxMode(self.filterView)
self.modeSelector.addItem('crossSelection') self.measureModes['Random Particle Selection'] = RandomMeasureMode(self.filterView)
self.modeSelector.addItem('Spiral Box Selection')
self.modeSelector.addItem('Cross Box Selection')
self.modeSelector.addItem('Random Box Selection')
self.modeSelector.addItem('Random Particle Selection')
for mode in self.measureModes.values():
mode.updatedResult.connect(self.infoWidget.update_results)
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())
...@@ -95,20 +106,46 @@ class MainView(QtWidgets.QWidget): ...@@ -95,20 +106,46 @@ class MainView(QtWidgets.QWidget):
self.filterView.filter.update_filterSize(width, height, diameter, (offsetx, offsety)) self.filterView.filter.update_filterSize(width, height, diameter, (offsetx, offsety))
for mode in self.measureModes.values(): for mode in self.measureModes.values():
mode.boxGenerator.particleContainer = dset.particleContainer mode.method.particleContainer = dset.particleContainer
mode.boxGenerator.offset = (offsetx, offsety) mode.method.offset = (offsetx, offsety)
self.filterView.update_from_dataset(dset) self.filterView.update_from_dataset(dset)
self.activeMode.update_measure_viewItems() self.activeMode.update_measure_viewItems()
self.infoWidget.samplename = dset.name
self.infoWidget.update_label()
def _update_fiter_rotation(self): def _update_filter_rotation(self):
self.filterView.update_rotation(self.rotationSpinBox.value()) self.filterView.update_rotation(self.rotationSpinBox.value())
self.activeMode.send_measuredParticles_to_filterview() self.activeMode.send_measuredParticles_to_filterview()
class SampleInfoWidget(QtWidgets.QLabel):
def __init__(self):
super(SampleInfoWidget, self).__init__()
self.samplename: str = ''
self.result: SubsamplingResult = None
self.update_label()
@QtCore.pyqtSlot(SubsamplingResult)
def update_results(self, result: SubsamplingResult):
self.result = result
self.update_label()
def update_label(self) -> None:
if self.samplename == '':
self.setText('No sample loaded')
else:
mpFrac: float = round(self.result.origMPCount / self.result.origParticleCount * 100, 1)
self.setText(f'Sample: {self.samplename}, {self.result.origParticleCount} particles, '
f'MP Fraction = {mpFrac} %, '
f'MP Count Error = {round(self.result.mpCountError, 1)} % '
f'| MP Particle Count: Orig: {self.result.origMPCount}, '
f'Estimated: {round(self.result.estimMPCount)}')
if __name__ == '__main__': if __name__ == '__main__':
import sys import sys
app = QtWidgets.QApplication(sys.argv) app = QtWidgets.QApplication(sys.argv)
subsampling = MainView() subsampling = MainView()
subsampling.show() subsampling.show()
ret = app.exec_() ret = app.exec_()
\ No newline at end of file
from PyQt5 import QtCore, QtWidgets from PyQt5 import QtCore, QtWidgets
from gui.filterView import FilterView, MeasureBoxGraphItem from gui.filterView import FilterView, MeasureBoxGraphItem
from geometricMethods import BoxSelectionSubsamplingMethod, CrossBoxSubSampling, SpiralBoxSubsampling from methods import *
from geometricMethods import *
from evaluation import SubsamplingResult
class MeasureMode(QtCore.QObject): class MeasureMode(QtCore.QObject):
def __init__(self, relatedFilterView: FilterView): updatedResult: QtCore.pyqtSignal = QtCore.pyqtSignal(SubsamplingResult)
def __init__(self, relatedFilterView: FilterView) -> None:
super(MeasureMode, self).__init__() super(MeasureMode, self).__init__()
self.filterView: FilterView = relatedFilterView self.filterView: FilterView = relatedFilterView
self.uiControls: QtWidgets.QGroupBox = QtWidgets.QGroupBox() self.uiControls: QtWidgets.QGroupBox = QtWidgets.QGroupBox()
self.boxGenerator: BoxSelectionSubsamplingMethod = None self.method: SubsamplingMethod = None
self.subsamplingResult: SubsamplingResult = SubsamplingResult(self.method)
self.subParticles: list = [] self.subParticles: list = []
def get_control_groupBox(self) -> QtWidgets.QGroupBox: def get_control_groupBox(self) -> QtWidgets.QGroupBox:
return self.uiControls return self.uiControls
def update_measure_viewItems(self) -> None: def update_measure_viewItems(self) -> None:
raise NotImplementedError self.method.filterDiameter = self.filterView.filter.diameter
self.method.numBoxes = self.uiControls.numBoxesSpinbox.value()
self.method.fraction = self.uiControls.coverageSpinbox.value() / 100
topLefts: list = self.method.get_topLeft_of_boxes()
boxSize = self.method.boxSize
self.filterView.update_measure_boxes(topLefts, boxSize)
self.send_measuredParticles_to_filterview()
def send_measuredParticles_to_filterview(self) -> None: def send_measuredParticles_to_filterview(self) -> None:
if self.boxGenerator.particleContainer is not None: if self.method.particleContainer is not None:
subParticles = self.boxGenerator.apply_subsampling_method() subParticles = self.method.apply_subsampling_method()
self.subsamplingResult.method = self.method
self.subsamplingResult.reset_results()
self.subsamplingResult.add_result(self.method.particleContainer.particles, subParticles)
self.updatedResult.emit(self.subsamplingResult)
self.filterView.update_measured_particles(subParticles) self.filterView.update_measured_particles(subParticles)
class RandomMeasureMode(MeasureMode):
updatedResult: QtCore.pyqtSignal = QtCore.pyqtSignal(SubsamplingResult)
def __init__(self, filterView: FilterView):
super(RandomMeasureMode, self).__init__(filterView)
self.method: RandomSampling = RandomSampling(None)
self.uiControls = ParticleModeControlGroup(self, 'Random Particle Measurement')
def update_measure_viewItems(self) -> None:
self.method.fraction = self.uiControls.coverageSpinbox.value() / 100
self.filterView.update_measure_boxes([], 0.0)
self.send_measuredParticles_to_filterview()
class ParticleModeControlGroup(QtWidgets.QGroupBox):
def __init__(self, measureModeParent: MeasureMode, title: str) -> None:
super(ParticleModeControlGroup, self).__init__()
self.measureModeParent = measureModeParent
self.setTitle(title)
layout = QtWidgets.QHBoxLayout()
self.setLayout(layout)
layout.addWidget(QtWidgets.QLabel('Desired Coverage (%)'))
self.coverageSpinbox = QtWidgets.QSpinBox()
self.coverageSpinbox.setFixedWidth(50)
self.coverageSpinbox.setMinimum(0)
self.coverageSpinbox.setMaximum(100)
self.coverageSpinbox.setValue(10)
self.coverageSpinbox.valueChanged.connect(self._config_changed)
layout.addWidget(self.coverageSpinbox)
def _config_changed(self) -> None:
self.measureModeParent.update_measure_viewItems()
class CrossBoxMode(MeasureMode): class CrossBoxMode(MeasureMode):
def __init__(self, *args): def __init__(self, *args):
super(CrossBoxMode, self).__init__(*args) super(CrossBoxMode, self).__init__(*args)
self.uiControls = CrossBoxesControls(self) self.uiControls = CrossBoxesControls(self)
self.boxGenerator: CrossBoxSubSampling = CrossBoxSubSampling(None) self.method: CrossBoxSubSampling = CrossBoxSubSampling(None)
self.update_measure_viewItems() self.update_measure_viewItems()
def update_measure_viewItems(self) -> None: def update_measure_viewItems(self) -> None:
self.boxGenerator.filterDiameter = self.filterView.filter.diameter self.method.filterDiameter = self.filterView.filter.diameter
self.boxGenerator.numBoxesAcross = int(self.uiControls.numBoxesSelector.currentText()) self.method.numBoxesAcross = int(self.uiControls.numBoxesSelector.currentText())
desiredCoverage: int = self.uiControls.coverageSpinbox.value() desiredCoverage: int = self.uiControls.coverageSpinbox.value()
maxCoverage: int = int(self.boxGenerator.get_maximum_achievable_fraction() * 100) maxCoverage: int = int(self.method.get_maximum_achievable_fraction() * 100)
self.uiControls.set_to_max_possible_coverage(maxCoverage) self.uiControls.set_to_max_possible_coverage(maxCoverage)
if desiredCoverage > maxCoverage: if desiredCoverage > maxCoverage:
desiredCoverage = maxCoverage desiredCoverage = maxCoverage
self.boxGenerator.fraction = desiredCoverage / 100 self.method.fraction = desiredCoverage / 100
topLefts: list = self.boxGenerator.get_topLeft_of_boxes() topLefts: list = self.method.get_topLeft_of_boxes()
boxSize = self.boxGenerator.boxSize boxSize = self.method.boxSize
self.filterView.update_measure_boxes(topLefts, boxSize) self.filterView.update_measure_boxes(topLefts, boxSize)
self.send_measuredParticles_to_filterview() self.send_measuredParticles_to_filterview()
...@@ -88,33 +141,14 @@ class CrossBoxesControls(QtWidgets.QGroupBox): ...@@ -88,33 +141,14 @@ class CrossBoxesControls(QtWidgets.QGroupBox):
self.coverageSpinbox.valueChanged.connect(self._config_changed) self.coverageSpinbox.valueChanged.connect(self._config_changed)
class SpiralBoxMode(MeasureMode): class BoxControlGroup(QtWidgets.QGroupBox):
def __init__(self, *args):
super(SpiralBoxMode, self).__init__(*args)
self.uiControls: SpiralBoxControls = SpiralBoxControls(self)
self.boxGenerator: SpiralBoxSubsampling = SpiralBoxSubsampling(None)
self.update_measure_viewItems()
def update_measure_viewItems(self) -> None:
self.boxGenerator.filterDiameter = self.filterView.filter.diameter
self.boxGenerator.numBoxes = self.uiControls.numBoxesSpinbox.value()
self.boxGenerator.fraction = self.uiControls.coverageSpinbox.value() / 100
topLefts: list = self.boxGenerator.get_topLeft_of_boxes()
boxSize = self.boxGenerator.boxSize
self.filterView.update_measure_boxes(topLefts, boxSize)
self.send_measuredParticles_to_filterview()
class SpiralBoxControls(QtWidgets.QGroupBox):
""" """
Gives a groupbox with the controls for setting up the cross boxes. Gives a groupbox with the controls for setting up the cross boxes.
""" """
def __init__(self, measureModeParent: MeasureMode): def __init__(self, measureModeParent: MeasureMode, title: str) -> None:
super(SpiralBoxControls, self).__init__() super(BoxControlGroup, self).__init__()
self.setTitle('Spiral Box Controls') self.setTitle(title)
self.measureModeParent = measureModeParent self.measureModeParent = measureModeParent
layout = QtWidgets.QHBoxLayout() layout = QtWidgets.QHBoxLayout()
self.setLayout(layout) self.setLayout(layout)
...@@ -136,6 +170,38 @@ class SpiralBoxControls(QtWidgets.QGroupBox): ...@@ -136,6 +170,38 @@ class SpiralBoxControls(QtWidgets.QGroupBox):
self.coverageSpinbox.valueChanged.connect(self._config_changed) self.coverageSpinbox.valueChanged.connect(self._config_changed)
layout.addWidget(self.coverageSpinbox) layout.addWidget(self.coverageSpinbox)
def _config_changed(self): def _config_changed(self) -> None:
if self.numBoxesSpinbox.value() > 0: if self.numBoxesSpinbox.value() > 0:
numBoxes: int = self.numBoxesSpinbox.value()
self.measureModeParent.method.numBoxes = numBoxes
maxCoverage: float = self.measureModeParent.method.get_maximum_achievable_fraction()
self.set_to_max_possible_coverage(round(maxCoverage * 100))
self.measureModeParent.update_measure_viewItems() self.measureModeParent.update_measure_viewItems()
def set_to_max_possible_coverage(self, maxCoverage: int) -> None:
"""
Adjusts maximum of coverage spinbox and, if necessary, caps the current value.
:param maxCoverage: the maximum pssible converage IN PERCENT!!
:return:
"""
self.coverageSpinbox.setMaximum(maxCoverage)
if maxCoverage < self.coverageSpinbox.value():
self.coverageSpinbox.valueChanged.disconnect()
self.coverageSpinbox.setValue(maxCoverage)
self.coverageSpinbox.valueChanged.connect(self._config_changed)
class SpiralBoxMode(MeasureMode):
def __init__(self, *args):
super(SpiralBoxMode, self).__init__(*args)
self.uiControls: BoxControlGroup = BoxControlGroup(self, 'Spiral Box Controls')
self.method: SpiralBoxSubsampling = SpiralBoxSubsampling(None)
self.update_measure_viewItems()
class RandomBoxMode(MeasureMode):
def __init__(self, *args):
super(RandomBoxMode, self).__init__(*args)
self.uiControls: BoxControlGroup = BoxControlGroup(self, 'Random Box Controls')
self.method: RandomBoxSampling = RandomBoxSampling(None)
self.update_measure_viewItems()
...@@ -23,7 +23,6 @@ def timingDecorator(callingFunction): ...@@ -23,7 +23,6 @@ def timingDecorator(callingFunction):
class ParticleBinSorter(object): class ParticleBinSorter(object):
def __init__(self): def __init__(self):
super(ParticleBinSorter, self).__init__() super(ParticleBinSorter, self).__init__()
self.bins = [5, 10, 20, 50, 100, 200, 500] self.bins = [5, 10, 20, 50, 100, 200, 500]
......
...@@ -11,6 +11,8 @@ from helpers import ParticleBinSorter ...@@ -11,6 +11,8 @@ from helpers import ParticleBinSorter
class SubsamplingMethod(object): class SubsamplingMethod(object):
randomSeed = 15203018
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
...@@ -65,6 +67,14 @@ class SubsamplingMethod(object): ...@@ -65,6 +67,14 @@ class SubsamplingMethod(object):
matches = (self.label.lower().find(pattern.lower()) != -1) matches = (self.label.lower().find(pattern.lower()) != -1)
return matches return matches
def config_is_valid(self) -> bool:
isValid: bool = False
if self.fraction <= self.get_maximum_achievable_fraction():
isValid = True
return isValid
def get_maximum_achievable_fraction(self) -> float:
raise NotImplementedError
class RandomSampling(SubsamplingMethod): class RandomSampling(SubsamplingMethod):
@property @property
...@@ -82,6 +92,9 @@ class RandomSampling(SubsamplingMethod): ...@@ -82,6 +92,9 @@ class RandomSampling(SubsamplingMethod):
def equals(self, otherMethod) -> bool: def equals(self, otherMethod) -> bool:
return type(otherMethod) == type(self) and otherMethod.fraction == self.fraction return type(otherMethod) == type(self) and otherMethod.fraction == self.fraction
def get_maximum_achievable_fraction(self) -> float:
return 1.0
class SizeBinFractioning(SubsamplingMethod): class SizeBinFractioning(SubsamplingMethod):
...@@ -117,3 +130,6 @@ class SizeBinFractioning(SubsamplingMethod): ...@@ -117,3 +130,6 @@ class SizeBinFractioning(SubsamplingMethod):
def equals(self, otherMethod) -> bool: def equals(self, otherMethod) -> bool:
return type(otherMethod) == type(self) and otherMethod.fraction == self.fraction return type(otherMethod) == type(self) and otherMethod.fraction == self.fraction
def get_maximum_achievable_fraction(self) -> float:
return 1.0
\ No newline at end of file
import matplotlib.pyplot as plt # import matplotlib.pyplot as plt
from matplotlib.figure import Figure
import time import time
from evaluation import TotalResults, SampleResult from evaluation import TotalResults, SampleResult
from input_output import get_pkls_from_directory, get_attributes_from_foldername, save_results, load_results from input_output import get_pkls_from_directory, get_attributes_from_foldername, save_results, load_results
from graphs import get_error_vs_frac_plot
""" """
IMPORTANT!!! IMPORTANT!!!
...@@ -10,58 +12,23 @@ SET GEPARD TO EVALUATION BRANCH (WITHOUT THE TILING STUFF), OTHERWISE SOME OF TH ...@@ -10,58 +12,23 @@ SET GEPARD TO EVALUATION BRANCH (WITHOUT THE TILING STUFF), OTHERWISE SOME OF TH
""" """
if __name__ == '__main__': if __name__ == '__main__':
results: TotalResults = TotalResults() # results: TotalResults = TotalResults()
pklsInFolders = get_pkls_from_directory(r'C:\Users\xbrjos\Desktop\temp MP\NewDatasets') # pklsInFolders = get_pkls_from_directory(r'C:\Users\xbrjos\Desktop\temp MP\NewDatasets')
#
for folder in pklsInFolders.keys(): # for folder in pklsInFolders.keys():
for samplePath in pklsInFolders[folder]: # for samplePath in pklsInFolders[folder]:
newSampleResult: SampleResult = results.add_sample(samplePath) # newSampleResult: SampleResult = results.add_sample(samplePath)
for attr in get_attributes_from_foldername(folder): # for attr in get_attributes_from_foldername(folder):
newSampleResult.set_attribute(attr) # newSampleResult.set_attribute(attr)
#
t0 = time.time() # t0 = time.time()
results.update_all() # results.update_all()
print('updating all took', time.time()-t0, 'seconds') # print('updating all took', time.time()-t0, 'seconds')
#
save_results('results1.res', results) # save_results('results1.res', results)
# results: TotalResults = load_results('results1.res') results: TotalResults = load_results('results1.res')
# save_results('results1.res', results) # save_results('results1.res', results)
plt.clf() plot: Figure = get_error_vs_frac_plot(results, attributes=[['air', 'water'], ['sediment', 'soil', 'beach', 'slush']],
errorPerFraction: dict = results.get_error_vs_fraction_data(attributes=['air', 'water'], methods=[['random', 'size']]*2)
methods=['random', 'sizeBin', 'chemo']) plot.show()
\ No newline at end of file
plt.subplot(121)
for methodLabel in errorPerFraction.keys():
errorDict: dict = errorPerFraction[methodLabel]
fractions: list = list(errorDict.keys())
errors: list = [errorDict[fraction][0] for fraction in fractions]
stdevs: list = [errorDict[fraction][1] for fraction in fractions]
plt.errorbar(fractions, errors, stdevs, label=methodLabel, marker='s', capsize=5)
plt.title('Air/Water sample', fontSize=15)
plt.xscale('log')
plt.xlabel('measured fraction', fontsize=12)
plt.ylabel('mpCountError (%)', fontsize=12)
plt.ylim([0, 100])
plt.legend()
errorPerFraction: dict = results.get_error_vs_fraction_data(attributes=['sediment', 'soil', 'beach', 'slush'],
methods=['random', 'sizeBin', 'chemo'])
plt.subplot(122)
for methodLabel in errorPerFraction.keys():
errorDict: dict = errorPerFraction[methodLabel]
fractions: list = list(errorDict.keys())
errors: list = [errorDict[fraction][0] for fraction in fractions]
stdevs: list = [errorDict[fraction][1] for fraction in fractions]
plt.errorbar(fractions, errors, stdevs, label=methodLabel, marker='s', capsize=5)
plt.title('Sediment/Beach/Slush sample', fontSize=15)
plt.xscale('log')
plt.xlabel('measured fraction', fontsize=12)
plt.ylabel('mpCountError (%)', fontsize=12)
plt.ylim([0, 100])
plt.legend()
plt.show()
...@@ -28,3 +28,4 @@ def get_default_ParticleContainer() -> ParticleContainer: ...@@ -28,3 +28,4 @@ def get_default_ParticleContainer() -> ParticleContainer:
contours.append(np.array([[[x, 0]], [[x+10, 0]], [[x+10, 10]], [[x, 10]]], dtype=np.int32)) contours.append(np.array([[[x, 0]], [[x+10, 0]], [[x+10, 10]], [[x, 10]]], dtype=np.int32))
particleContainer.setParticleContours(contours) particleContainer.setParticleContours(contours)
return particleContainer return particleContainer
...@@ -258,23 +258,29 @@ class TestSampleResult(unittest.TestCase): ...@@ -258,23 +258,29 @@ class TestSampleResult(unittest.TestCase):
possibleRandomMethods = 2 possibleRandomMethods = 2
possibleCrossBoxMethods = 2 possibleCrossBoxMethods = 2
possibleSpiralBoxMethods = 3 possibleSpiralBoxMethods = 3
possibleChemometricMethods = 1 possibleRandomBoxMethods = 3
possibleChemometricMethods = 0
totalPossible = possibleCrossBoxMethods + possibleRandomMethods + \ totalPossible = possibleCrossBoxMethods + possibleRandomMethods + \
possibleSpiralBoxMethods + possibleChemometricMethods possibleSpiralBoxMethods + possibleChemometricMethods + \
possibleRandomBoxMethods
self.assertEqual(len(methods), totalPossible) self.assertEqual(len(methods), totalPossible)
self.assertTrue(containsMethod(methods, meth.RandomSampling(dset, desiredFraction))) self.assertTrue(containsMethod(methods, meth.RandomSampling(dset, desiredFraction)))
self.assertTrue(containsMethod(methods, meth.SizeBinFractioning(dset, desiredFraction))) self.assertTrue(containsMethod(methods, meth.SizeBinFractioning(dset, desiredFraction)))
self.assertTrue(containsMethod(methods, gmeth.CrossBoxSubSampling(dset, desiredFraction))) self.assertTrue(containsMethod(methods, gmeth.CrossBoxSubSampling(dset, desiredFraction)))
self.assertTrue(containsMethod(methods, gmeth.SpiralBoxSubsampling(dset, desiredFraction))) self.assertTrue(containsMethod(methods, gmeth.SpiralBoxSubsampling(dset, desiredFraction)))
self.assertTrue(containsMethod(methods, gmeth.RandomBoxSampling(dset, desiredFraction)))
desiredFraction = 0.5 desiredFraction = 0.5
methods = get_methods_to_test(dset, [desiredFraction]) methods = get_methods_to_test(dset, [desiredFraction])
possibleRandomMethods = 2 possibleRandomMethods = 2
possibleCrossBoxMethods = 1 possibleCrossBoxMethods = 1
possibleSpiralBoxMethods = 0 possibleSpiralBoxMethods = 0
possibleChemometricMethods = 1 possibleChemometricMethods = 0
possibleRandomBoxMethods = 0
totalPossible = possibleCrossBoxMethods + possibleRandomMethods + \ totalPossible = possibleCrossBoxMethods + possibleRandomMethods + \
possibleSpiralBoxMethods + possibleChemometricMethods possibleSpiralBoxMethods + possibleChemometricMethods + \
possibleRandomBoxMethods
self.assertEqual(len(methods), totalPossible) self.assertEqual(len(methods), totalPossible)
self.assertTrue(containsMethod(methods, meth.RandomSampling(dset, desiredFraction))) self.assertTrue(containsMethod(methods, meth.RandomSampling(dset, desiredFraction)))
self.assertTrue(containsMethod(methods, meth.SizeBinFractioning(dset, desiredFraction))) self.assertTrue(containsMethod(methods, meth.SizeBinFractioning(dset, desiredFraction)))
...@@ -286,9 +292,11 @@ class TestSampleResult(unittest.TestCase): ...@@ -286,9 +292,11 @@ class TestSampleResult(unittest.TestCase):
possibleRandomMethods = 2 possibleRandomMethods = 2
possibleCrossBoxMethods = 0 possibleCrossBoxMethods = 0
possibleSpiralBoxMethods = 0 possibleSpiralBoxMethods = 0
possibleChemometricMethods = 1 possibleRandomBoxMethods = 0
possibleChemometricMethods = 0
totalPossible = possibleCrossBoxMethods + possibleRandomMethods + \ totalPossible = possibleCrossBoxMethods + possibleRandomMethods + \
possibleSpiralBoxMethods + possibleChemometricMethods possibleSpiralBoxMethods + possibleChemometricMethods + \
possibleRandomBoxMethods
self.assertEqual(len(methods), totalPossible) self.assertEqual(len(methods), totalPossible)
self.assertTrue(containsMethod(methods, meth.RandomSampling(dset, desiredFraction))) self.assertTrue(containsMethod(methods, meth.RandomSampling(dset, desiredFraction)))
self.assertTrue(containsMethod(methods, meth.SizeBinFractioning(dset, desiredFraction))) self.assertTrue(containsMethod(methods, meth.SizeBinFractioning(dset, desiredFraction)))
...@@ -300,9 +308,11 @@ class TestSampleResult(unittest.TestCase): ...@@ -300,9 +308,11 @@ class TestSampleResult(unittest.TestCase):
possibleRandomMethods = 4 possibleRandomMethods = 4
possibleCrossBoxMethods = 3 possibleCrossBoxMethods = 3
possibleSpiralBoxMethods = 3 possibleSpiralBoxMethods = 3
possibleChemometricMethods = 2 possibleChemometricMethods = 0
possibleRandomBoxMethods = 3
totalPossible = possibleCrossBoxMethods + possibleRandomMethods + \ totalPossible = possibleCrossBoxMethods + possibleRandomMethods + \
possibleSpiralBoxMethods + possibleChemometricMethods possibleSpiralBoxMethods + possibleChemometricMethods + \
possibleRandomBoxMethods
self.assertEqual(len(methods), totalPossible) self.assertEqual(len(methods), totalPossible)
for desiredFraction in desiredFractions: for desiredFraction in desiredFractions:
self.assertTrue(containsMethod(methods, meth.RandomSampling(dset, desiredFraction))) self.assertTrue(containsMethod(methods, meth.RandomSampling(dset, desiredFraction)))
...@@ -363,17 +373,25 @@ class TestSubsamplingResult(unittest.TestCase): ...@@ -363,17 +373,25 @@ class TestSubsamplingResult(unittest.TestCase):
self.subsamplingResult.add_result(origParticles, subParticles) self.subsamplingResult.add_result(origParticles, subParticles)
self.assertEqual(len(self.subsamplingResult.mpCountErrors), 1) self.assertEqual(len(self.subsamplingResult.mpCountErrors), 1)
self.assertEqual(self.subsamplingResult.mpCountErrors[0], 50) self.assertEqual(self.subsamplingResult.mpCountErrors[0], 50)
self.assertEqual(self.subsamplingResult.origMPCount, 100)
self.assertEqual(self.subsamplingResult.estimMPCounts, [150])
self.assertAlmostEqual(self.subsamplingResult.estimMPCount, 150)
subParticles = self._get_MP_particles(10) # at fraction of 0.1, 10 particles would be expected subParticles = self._get_MP_particles(10) # at fraction of 0.1, 10 particles would be expected
self.subsamplingResult.add_result(origParticles, subParticles) self.subsamplingResult.add_result(origParticles, subParticles)
self.assertEqual(len(self.subsamplingResult.mpCountErrors), 2) self.assertEqual(len(self.subsamplingResult.mpCountErrors), 2)
self.assertEqual(self.subsamplingResult.mpCountErrors[0], 50) self.assertEqual(self.subsamplingResult.mpCountErrors[0], 50)
self.assertEqual(self.subsamplingResult.mpCountErrors[1], 0) self.assertEqual(self.subsamplingResult.mpCountErrors[1], 0)
self.assertAlmostEqual(self.subsamplingResult.mpCountError, 25)
self.assertEqual(self.subsamplingResult.estimMPCounts, [150, 100])
self.assertEqual(self.subsamplingResult.estimMPCount, 125)
def test_reset_results(self): def test_reset_results(self):
self.subsamplingResult.mpCountErrors = [10, 30, 20] self.subsamplingResult.mpCountErrors = [10, 30, 20]
self.subsamplingResult.estimMPCounts = [2, 5, 3]
self.subsamplingResult.reset_results() self.subsamplingResult.reset_results()
self.assertEqual(self.subsamplingResult.mpCountErrors, []) self.assertEqual(self.subsamplingResult.mpCountErrors, [])
self.assertEqual(self.subsamplingResult.estimMPCounts, [])
def test_get_error_per_bin(self): def test_get_error_per_bin(self):
def get_full_and_sub_particles(): def get_full_and_sub_particles():
...@@ -450,7 +468,7 @@ class TestSubsamplingResult(unittest.TestCase): ...@@ -450,7 +468,7 @@ class TestSubsamplingResult(unittest.TestCase):
self.subsamplingResult.mpCountErrors = [50, 75, 100] self.subsamplingResult.mpCountErrors = [50, 75, 100]
self.assertAlmostEqual(self.subsamplingResult.mpCountErrorStDev, 20.412414523193153) self.assertAlmostEqual(self.subsamplingResult.mpCountErrorStDev, 20.412414523193153)
def test_get_averaged_errrs(self): def test_get_averaged_erros(self):
self.subsamplingResult.mpCountErrors = [50, 75, 100] self.subsamplingResult.mpCountErrors = [50, 75, 100]
self.assertEqual(self.subsamplingResult.mpCountError, 75) self.assertEqual(self.subsamplingResult.mpCountError, 75)
...@@ -484,6 +502,7 @@ class TestSubsamplingResult(unittest.TestCase): ...@@ -484,6 +502,7 @@ class TestSubsamplingResult(unittest.TestCase):
return nonMPParticles return nonMPParticles
def _get_MP_particle(self) -> Particle: def _get_MP_particle(self) -> Particle:
random.seed(15203018)
polymerNames = ['Poly (methyl methacrylate', polymerNames = ['Poly (methyl methacrylate',
'Polyethylene', 'Polyethylene',
'Silicone rubber', 'Silicone rubber',
......
import sys import sys
import unittest import unittest
import numpy as np import numpy as np
from geometricMethods import BoxSelectionSubsamplingMethod, CrossBoxSubSampling, SpiralBoxSubsampling, BoxSelectionCreator from geometricMethods import *
sys.path.append("C://Users//xbrjos//Desktop//Python") sys.path.append("C://Users//xbrjos//Desktop//Python")
from gepard import dataset from gepard import dataset
class TestBoxSelector(unittest.TestCase): class TestBoxSelector(unittest.TestCase):
def setUp(self) -> None: def setUp(self) -> None:
self.boxSelector: BoxSelectionSubsamplingMethod = BoxSelectionSubsamplingMethod(None) self.boxSelector: BoxSelectionSubsamplingMethod = BoxSelectionSubsamplingMethod(None)
...@@ -39,6 +37,21 @@ class TestSelectCrossBoxes(unittest.TestCase): ...@@ -39,6 +37,21 @@ class TestSelectCrossBoxes(unittest.TestCase):
def setUp(self) -> None: def setUp(self) -> None:
self.crossBoxSubsampler = CrossBoxSubSampling(None) self.crossBoxSubsampler = CrossBoxSubSampling(None)
def test_get_max_fraction(self):
self.crossBoxSubsampler.numBoxesAcross = 3
self.assertAlmostEqual(self.crossBoxSubsampler.get_maximum_achievable_fraction(), 0.5364176686762333)
self.crossBoxSubsampler.numBoxesAcross = 5
self.assertAlmostEqual(self.crossBoxSubsampler.get_maximum_achievable_fraction(), 0.3841550054130974)
def test_is_config_valid(self):
self.crossBoxSubsampler.numBoxesAcross = 3
self.crossBoxSubsampler.fraction = 0.5
self.assertTrue(self.crossBoxSubsampler.config_is_valid())
self.crossBoxSubsampler.fraction = 0.55
self.assertFalse(self.crossBoxSubsampler.config_is_valid())
def test_get_topLeft_of_boxes(self): def test_get_topLeft_of_boxes(self):
self.crossBoxSubsampler.filterDiameter = 100 self.crossBoxSubsampler.filterDiameter = 100
self.crossBoxSubsampler.fraction = 0.1 self.crossBoxSubsampler.fraction = 0.1
...@@ -112,22 +125,36 @@ class TestSelectSpiralBoxes(unittest.TestCase): ...@@ -112,22 +125,36 @@ class TestSelectSpiralBoxes(unittest.TestCase):
self.assertEqual(newTopLefts[1], (0, 45)) self.assertEqual(newTopLefts[1], (0, 45))
self.assertEqual(newTopLefts[2], (90, 45)) self.assertEqual(newTopLefts[2], (90, 45))
def test_get_max_distance_of_boxCenter_to_center(self): def test_get_max_fraction(self):
for numBoxes in self.spiralBoxSubsampler.possibleBoxNumbers:
self.spiralBoxSubsampler.numBoxes = numBoxes
maxFrac: float = determine_max_achievable_frac(self.spiralBoxSubsampler, numBoxes)
self.assertEqual(self.spiralBoxSubsampler.get_maximum_achievable_fraction(), maxFrac)
# now test for numBoxes that are not indicated in the possibleBoxNumbers class list.
# if assertion fails, please adjust values in below list to cover other numbers than tested before
for numBoxes in [1, 3, 6, 11]:
assert numBoxes not in self.spiralBoxSubsampler.possibleBoxNumbers
self.spiralBoxSubsampler.numBoxes = numBoxes
maxFrac: float = determine_max_achievable_frac(self.spiralBoxSubsampler, numBoxes)
self.assertEqual(self.spiralBoxSubsampler.get_maximum_achievable_fraction(), maxFrac)
def test_get_max_distance_of_box_to_center(self):
boxCenter = 0, 0 boxCenter = 0, 0
self._set_boxSelectorFraction_to_reach_boxSize(1) self._set_boxSelectorFraction_to_reach_boxSize(1)
maxDistance: float = self.spiralBoxSubsampler._get_max_distance_of_boxCenter_to_center(boxCenter) maxDistance: float = self.spiralBoxSubsampler._get_max_distance_of_box_to_center(boxCenter)
self.assertEqual(maxDistance, np.sqrt(0.5**2 + 0.5**2)) self.assertEqual(maxDistance, np.sqrt(0.5**2 + 0.5**2))
self._set_boxSelectorFraction_to_reach_boxSize(2) self._set_boxSelectorFraction_to_reach_boxSize(2)
maxDistance: float = self.spiralBoxSubsampler._get_max_distance_of_boxCenter_to_center(boxCenter) maxDistance: float = self.spiralBoxSubsampler._get_max_distance_of_box_to_center(boxCenter)
self.assertEqual(maxDistance, np.sqrt(2)) self.assertEqual(maxDistance, np.sqrt(2))
boxCenter = 1, 0 boxCenter = 1, 0
maxDistance: float = self.spiralBoxSubsampler._get_max_distance_of_boxCenter_to_center(boxCenter) maxDistance: float = self.spiralBoxSubsampler._get_max_distance_of_box_to_center(boxCenter)
self.assertEqual(maxDistance, np.sqrt(2**2 + 1**2)) self.assertEqual(maxDistance, np.sqrt(2**2 + 1**2))
boxCenter = -1, -3 boxCenter = -1, -3
maxDistance: float = self.spiralBoxSubsampler._get_max_distance_of_boxCenter_to_center(boxCenter) maxDistance: float = self.spiralBoxSubsampler._get_max_distance_of_box_to_center(boxCenter)
self.assertEqual(maxDistance, np.sqrt(4**2 + 2**2)) self.assertEqual(maxDistance, np.sqrt(4**2 + 2**2))
def test_boxes_are_overlapping(self): def test_boxes_are_overlapping(self):
...@@ -161,6 +188,43 @@ class TestSelectSpiralBoxes(unittest.TestCase): ...@@ -161,6 +188,43 @@ class TestSelectSpiralBoxes(unittest.TestCase):
assert self.spiralBoxSubsampler.boxSize == desiredBoxSize assert self.spiralBoxSubsampler.boxSize == desiredBoxSize
class TestRandomBoxes(unittest.TestCase):
def setUp(self) -> None:
self.randBoxes: RandomBoxSampling = RandomBoxSampling(None, 0.1)
self.randBoxes.maxTries = 10
def test_get_top_lefts(self):
self.randBoxes.update_max_fractions()
radius: float = self.randBoxes.filterDiameter/2
center: tuple = radius, radius
for frac in [0.1, 0.3, 0.5]:
self.randBoxes.fraction = frac
for numBoxes in self.randBoxes.possibleBoxNumbers:
self.randBoxes.numBoxes = numBoxes
if self.randBoxes.config_is_valid():
topLefts: list = self.randBoxes.get_topLeft_of_boxes()
self.assertEqual(len(topLefts), numBoxes)
boxSize: float = self.randBoxes.boxSize
for topLeft in topLefts:
points: list = [[topLeft[0], topLeft[1]], [topLeft[0], topLeft[1]+boxSize],
[topLeft[0]+boxSize, topLeft[1]], [topLeft[0]+boxSize, topLeft[1]+boxSize]]
for point in points:
dist = np.linalg.norm([point[0]-center[0], point[1]-center[1]])
self.assertTrue(point[0] >= 0 and point[1] >= 0)
self.assertTrue(dist <= radius)
def test_label(self):
self.assertTrue(type(self.randBoxes.label), str)
def test_get_max_fraction(self):
for numBoxes in self.randBoxes.possibleBoxNumbers:
self.randBoxes.numBoxes = numBoxes
maxFrac: float = determine_max_achievable_frac(self.randBoxes, numBoxes)
self.assertEqual(self.randBoxes.get_maximum_achievable_fraction(), maxFrac)
class TestBoxCreator(unittest.TestCase): class TestBoxCreator(unittest.TestCase):
def setUp(self) -> None: def setUp(self) -> None:
self.dataset: dataset.DataSet = dataset.DataSet('test') self.dataset: dataset.DataSet = dataset.DataSet('test')
...@@ -178,6 +242,8 @@ class TestBoxCreator(unittest.TestCase): ...@@ -178,6 +242,8 @@ class TestBoxCreator(unittest.TestCase):
desiredFraction: float = 0.1 desiredFraction: float = 0.1
crossBoxSubsamplers: list = self.boxCreator.get_crossBoxSubsamplers_for_fraction(desiredFraction) crossBoxSubsamplers: list = self.boxCreator.get_crossBoxSubsamplers_for_fraction(desiredFraction)
self._assert_correct_partCont(crossBoxSubsamplers)
self.assertEqual(len(crossBoxSubsamplers), 2) self.assertEqual(len(crossBoxSubsamplers), 2)
numBoxesAcrossList: list = [] numBoxesAcrossList: list = []
for boxSelector in crossBoxSubsamplers: for boxSelector in crossBoxSubsamplers:
...@@ -190,25 +256,55 @@ class TestBoxCreator(unittest.TestCase): ...@@ -190,25 +256,55 @@ class TestBoxCreator(unittest.TestCase):
self.assertTrue(5 in numBoxesAcrossList) self.assertTrue(5 in numBoxesAcrossList)
desiredFraction = 0.5 # only the version with 3 boxes works here desiredFraction = 0.5 # only the version with 3 boxes works here
crossBoxSubsamplers: list = self.boxCreator.get_crossBoxSubsamplers_for_fraction(desiredFraction) crossBoxSubsamplers = self.boxCreator.get_crossBoxSubsamplers_for_fraction(desiredFraction)
self._assert_correct_partCont(crossBoxSubsamplers)
self.assertEqual(len(crossBoxSubsamplers), 1) self.assertEqual(len(crossBoxSubsamplers), 1)
self.assertEqual(crossBoxSubsamplers[0].numBoxesAcross, 3) self.assertEqual(crossBoxSubsamplers[0].numBoxesAcross, 3)
desiredFraction = 0.55 # no boxes work for that. desiredFraction = 0.55 # no boxes work for that.
crossBoxSubsamplers: list = self.boxCreator.get_crossBoxSubsamplers_for_fraction(desiredFraction) crossBoxSubsamplers = self.boxCreator.get_crossBoxSubsamplers_for_fraction(desiredFraction)
self.assertEqual(len(crossBoxSubsamplers), 0) self.assertEqual(len(crossBoxSubsamplers), 0)
def test_get_spiralBoxSubsamplers_for_fraction(self): def test_get_spiralBoxSubsamplers_for_fraction(self):
desiredFraction: float = 0.1 desiredFraction: float = 0.1
spiralBoxSubsamplers = self.boxCreator.get_spiralBoxSubsamplers_for_fraction(desiredFraction) spiralBoxSubsamplers = self.boxCreator.get_spiralBoxSubsamplers_for_fraction(desiredFraction)
self._assert_correct_partCont(spiralBoxSubsamplers)
self.assertTrue(len(spiralBoxSubsamplers) == len(SpiralBoxSubsampling.possibleBoxNumbers)) # all boxNumbers work here self.assertTrue(len(spiralBoxSubsamplers) == len(SpiralBoxSubsampling.possibleBoxNumbers)) # all boxNumbers work here
for selector in spiralBoxSubsamplers: for selector in spiralBoxSubsamplers:
self.assertEqual(selector.boxSize, self._getBoxSize(selector.numBoxes, desiredFraction)) self.assertEqual(selector.boxSize, self._getBoxSize(selector.numBoxes, desiredFraction))
desiredFraction: float = 0.5 desiredFraction: float = 0.4
spiralBoxSubsamplers = self.boxCreator.get_spiralBoxSubsamplers_for_fraction(desiredFraction) spiralBoxSubsamplers = self.boxCreator.get_spiralBoxSubsamplers_for_fraction(desiredFraction)
self.assertTrue(len(spiralBoxSubsamplers) == 0) # no boxNumbers work here self.assertTrue(len(spiralBoxSubsamplers) == 0) # no boxNumbers work here
def test_get_randBoxSubsampler_for_Fraction(self):
randBoxSampler: RandomBoxSampling = RandomBoxSampling(None)
randBoxSampler.update_max_fractions()
maxFracs: dict = randBoxSampler.maxFractions
for frac in np.linspace(0.1, 0.6, 5):
numValid: int = 0
validNumBoxes: list = []
for numBoxes in randBoxSampler.possibleBoxNumbers:
if maxFracs[numBoxes] >= frac:
numValid += 1
validNumBoxes.append(numBoxes)
possibleMehotds: list = self.boxCreator.get_randomBoxSubsamplers_for_fraction(frac)
self.assertEqual(len(possibleMehotds), numValid)
self._assert_correct_partCont(possibleMehotds)
for meth in possibleMehotds:
self.assertTrue(meth.numBoxes in validNumBoxes)
def _assert_correct_partCont(self, listOfMethods: list) -> None:
"""
Iterates through the list of subsampling methods and tests if the particle Container is set correctly
:param listOfMethods:
:return:
"""
for partCont in [sampler.particleContainer for sampler in listOfMethods]:
self.assertTrue(partCont is self.dataset.particleContainer)
def _getBoxSize(self, numBoxes: int, desiredFraction: float) -> float: def _getBoxSize(self, numBoxes: int, desiredFraction: float) -> float:
""" """
Calculates the size of each box (= width = height), given a certain filter area and number of Boxes Calculates the size of each box (= width = height), given a certain filter area and number of Boxes
......