...
 
Commits (3)
......@@ -13,6 +13,7 @@ import concurrent.futures
import sys
sys.path.append("C://Users//xbrjos//Desktop//Python")
from gepard import dataset
from gepard.analysis.particleAndMeasurement import Particle
from helpers import ParticleBinSorter
import methods as meth
......@@ -42,7 +43,8 @@ def get_methods_to_test(dataset: dataset.DataSet, fractions: list = []) -> list:
boxCreator: gmeth.BoxSelectionCreator = gmeth.BoxSelectionCreator(dataset)
methods += boxCreator.get_crossBoxSubsamplers_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
......@@ -53,6 +55,17 @@ def update_sample(sample, force: bool, index: int):
sample.update_result_with_methods(methods, force)
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):
def __init__(self):
......@@ -85,7 +98,8 @@ class TotalResults(object):
indices: list = list(np.arange(len(self.sampleResults)))
numSamples: int = len(forceList)
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:
results = executor.map(update_sample, self.sampleResults, forceList, indices, chunksize=chunksize)
......@@ -137,15 +151,17 @@ class SubsamplingResult(object):
"""
Stores all interesting results from a subsampling experiment
"""
# TODO: UPDATE PATTERNS -> ARE THESE REASONABLE???
mpPatterns = ['poly', 'rubber', 'pb', 'pr', 'pg', 'py', 'pv']
# # # TODO: UPDATE PATTERNS -> ARE THESE REASONABLE???
# mpPatterns = ['poly', 'rubber', 'pb', 'pr', 'pg', 'py', 'pv']
def __init__(self, subsamplingMethod: meth.SubsamplingMethod):
super(SubsamplingResult, self).__init__()
self.method: meth.SubsamplingMethod = subsamplingMethod
self.mpCountErrors: list = []
# self.origParticleCount: int = None
# self.subSampledParticleCount: int = None
self.origParticleCount: int = 0
self.subSampledParticleCount: int = 0
self.origMPCount: int = 0
self.estimMPCounts: list = []
# self.mpCountErrorPerBin: tuple = None
@property
......@@ -159,15 +175,20 @@ class SubsamplingResult(object):
def mpCountErrorStDev(self) -> float:
stdev: float = 0.0
if len(self.mpCountErrors) > 0:
stdev = np.std(self.mpCountErrors)
stdev = float(np.std(self.mpCountErrors))
return stdev
@property
def estimMPCount(self) -> float:
return float(np.mean(self.estimMPCounts))
def reset_results(self) -> None:
"""
Deletes all results
:return:
"""
self.mpCountErrors = []
self.estimMPCounts = []
def add_result(self, origParticles: list, subParticles: list) -> None:
"""
......@@ -181,6 +202,7 @@ class SubsamplingResult(object):
# error: float = self._get_mp_count_error(origParticles, subParticles, 1.0)
# else:
error: float = self._get_mp_count_error(origParticles, subParticles, self.method.fraction)
self.origParticleCount = len(origParticles)
self.mpCountErrors.append(error)
def _get_mp_count_error_per_bin(self, allParticles: list, subParticles: list, fractionMeasured: float) -> tuple:
......@@ -194,7 +216,9 @@ class SubsamplingResult(object):
def _get_mp_count_error(self, allParticles: list, subParticles: list, fractionMeasured: float) -> float:
numMPOrig = self._get_number_of_MP_particles(allParticles)
self.origMPCount = numMPOrig
numMPEstimate = self._get_number_of_MP_particles(subParticles) / fractionMeasured
self.estimMPCounts.append(numMPEstimate)
if numMPOrig != 0:
mpCountError = self._get_error_from_values(numMPOrig, numMPEstimate)
......@@ -212,12 +236,8 @@ class SubsamplingResult(object):
def _get_number_of_MP_particles(self, particleList: list) -> int:
numMPParticles = 0
for particle in particleList:
assignment = particle.getParticleAssignment()
for pattern in self.mpPatterns:
if assignment.lower().find(pattern) != -1:
numMPParticles += 1
break
if is_MP_particle(particle):
numMPParticles += 1
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
from gepard import dataset
import helpers
from cythonModules import rotateContour as rc
from evaluation import is_MP_particle
class FilterView(QtWidgets.QGraphicsView):
......@@ -65,7 +65,7 @@ class FilterView(QtWidgets.QGraphicsView):
self._remove_particle_contours()
if self.dataset is not None:
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.contourItems.append(newContour)
......@@ -167,12 +167,13 @@ class MeasureBoxGraphItem(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__()
self.setZValue(1)
self.setPos(pos[0], pos[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.contourData = contourData
self.polygon = None
......@@ -199,11 +200,13 @@ class ParticleContour(QtWidgets.QGraphicsItem):
def paint(self, painter, option, widget) -> None:
if self.polygon is not None:
if self.isMeasured:
if self.isMP:
painter.setPen(QtCore.Qt.darkRed)
painter.setBrush(QtCore.Qt.red)
else:
painter.setPen(QtCore.Qt.darkCyan)
painter.setBrush(QtCore.Qt.cyan)
painter.setPen(QtCore.Qt.darkBlue)
painter.setBrush(QtCore.Qt.blue)
painter.setOpacity(1 if self.isMeasured else 0.2)
painter.drawPolygon(self.polygon)
from PyQt5 import QtWidgets
from PyQt5 import QtWidgets, QtCore
import sys
sys.path.append("C://Users//xbrjos//Desktop//Python")
import gepard
from gepard import dataset
from gui.filterView import FilterView
from gui.measureModes import MeasureMode, CrossBoxMode, CrossBoxesControls, SpiralBoxMode
from gui.measureModes import *
import helpers
from evaluation import SubsamplingResult
class MainView(QtWidgets.QWidget):
......@@ -30,7 +31,7 @@ class MainView(QtWidgets.QWidget):
self.rotationSpinBox.setMaximum(359)
self.rotationSpinBox.setValue(0)
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.controlGroupLayout = QtWidgets.QHBoxLayout()
......@@ -44,17 +45,27 @@ class MainView(QtWidgets.QWidget):
self.controlGroupLayout.addWidget(self.activeModeControl)
self.controlGroupLayout.addStretch()
self.infoWidget: SampleInfoWidget = SampleInfoWidget()
self.layout.addWidget(self.controlGroup)
self.layout.addWidget(self.infoWidget)
self.filterView = FilterView()
self.layout.addWidget(self.filterView)
self._add_measure_modes()
self._switch_to_default_mode()
def _add_measure_modes(self) -> None:
self.measureModes['spiralSelection'] = SpiralBoxMode(self.filterView)
self.measureModes['crossSelection'] = CrossBoxMode(self.filterView)
self.modeSelector.addItem('spiralSelection')
self.modeSelector.addItem('crossSelection')
self.measureModes['Spiral Box Selection'] = SpiralBoxMode(self.filterView)
self.measureModes['Cross Box Selection'] = CrossBoxMode(self.filterView)
self.measureModes['Random Box Selection'] = RandomBoxMode(self.filterView)
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:
modes: list = list(self.measureModes.keys())
......@@ -95,20 +106,46 @@ class MainView(QtWidgets.QWidget):
self.filterView.filter.update_filterSize(width, height, diameter, (offsetx, offsety))
for mode in self.measureModes.values():
mode.boxGenerator.particleContainer = dset.particleContainer
mode.boxGenerator.offset = (offsetx, offsety)
mode.method.particleContainer = dset.particleContainer
mode.method.offset = (offsetx, offsety)
self.filterView.update_from_dataset(dset)
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.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__':
import sys
app = QtWidgets.QApplication(sys.argv)
subsampling = MainView()
subsampling.show()
ret = app.exec_()
\ No newline at end of file
ret = app.exec_()
from PyQt5 import QtCore, QtWidgets
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):
def __init__(self, relatedFilterView: FilterView):
updatedResult: QtCore.pyqtSignal = QtCore.pyqtSignal(SubsamplingResult)
def __init__(self, relatedFilterView: FilterView) -> None:
super(MeasureMode, self).__init__()
self.filterView: FilterView = relatedFilterView
self.uiControls: QtWidgets.QGroupBox = QtWidgets.QGroupBox()
self.boxGenerator: BoxSelectionSubsamplingMethod = None
self.method: SubsamplingMethod = None
self.subsamplingResult: SubsamplingResult = SubsamplingResult(self.method)
self.subParticles: list = []
def get_control_groupBox(self) -> QtWidgets.QGroupBox:
return self.uiControls
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:
if self.boxGenerator.particleContainer is not None:
subParticles = self.boxGenerator.apply_subsampling_method()
if self.method.particleContainer is not None:
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)
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):
def __init__(self, *args):
super(CrossBoxMode, self).__init__(*args)
self.uiControls = CrossBoxesControls(self)
self.boxGenerator: CrossBoxSubSampling = CrossBoxSubSampling(None)
self.method: CrossBoxSubSampling = CrossBoxSubSampling(None)
self.update_measure_viewItems()
def update_measure_viewItems(self) -> None:
self.boxGenerator.filterDiameter = self.filterView.filter.diameter
self.boxGenerator.numBoxesAcross = int(self.uiControls.numBoxesSelector.currentText())
self.method.filterDiameter = self.filterView.filter.diameter
self.method.numBoxesAcross = int(self.uiControls.numBoxesSelector.currentText())
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)
if desiredCoverage > maxCoverage:
desiredCoverage = maxCoverage
self.boxGenerator.fraction = desiredCoverage / 100
self.method.fraction = desiredCoverage / 100
topLefts: list = self.boxGenerator.get_topLeft_of_boxes()
boxSize = self.boxGenerator.boxSize
topLefts: list = self.method.get_topLeft_of_boxes()
boxSize = self.method.boxSize
self.filterView.update_measure_boxes(topLefts, boxSize)
self.send_measuredParticles_to_filterview()
......@@ -88,33 +141,14 @@ class CrossBoxesControls(QtWidgets.QGroupBox):
self.coverageSpinbox.valueChanged.connect(self._config_changed)
class SpiralBoxMode(MeasureMode):
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):
class BoxControlGroup(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')
def __init__(self, measureModeParent: MeasureMode, title: str) -> None:
super(BoxControlGroup, self).__init__()
self.setTitle(title)
self.measureModeParent = measureModeParent
layout = QtWidgets.QHBoxLayout()
self.setLayout(layout)
......@@ -136,6 +170,38 @@ class SpiralBoxControls(QtWidgets.QGroupBox):
self.coverageSpinbox.valueChanged.connect(self._config_changed)
layout.addWidget(self.coverageSpinbox)
def _config_changed(self):
def _config_changed(self) -> None:
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()
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):
class ParticleBinSorter(object):
def __init__(self):
super(ParticleBinSorter, self).__init__()
self.bins = [5, 10, 20, 50, 100, 200, 500]
......
......@@ -11,6 +11,8 @@ from helpers import ParticleBinSorter
class SubsamplingMethod(object):
randomSeed = 15203018
def __init__(self, particleConatainer, desiredFraction: float = 0.2):
super(SubsamplingMethod, self).__init__()
self.particleContainer = particleConatainer
......@@ -65,6 +67,14 @@ class SubsamplingMethod(object):
matches = (self.label.lower().find(pattern.lower()) != -1)
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):
@property
......@@ -82,6 +92,9 @@ class RandomSampling(SubsamplingMethod):
def equals(self, otherMethod) -> bool:
return type(otherMethod) == type(self) and otherMethod.fraction == self.fraction
def get_maximum_achievable_fraction(self) -> float:
return 1.0
class SizeBinFractioning(SubsamplingMethod):
......@@ -117,3 +130,6 @@ class SizeBinFractioning(SubsamplingMethod):
def equals(self, otherMethod) -> bool:
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
from evaluation import TotalResults, SampleResult
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!!!
......@@ -10,58 +12,23 @@ SET GEPARD TO EVALUATION BRANCH (WITHOUT THE TILING STUFF), OTHERWISE SOME OF TH
"""
if __name__ == '__main__':
results: TotalResults = TotalResults()
pklsInFolders = get_pkls_from_directory(r'C:\Users\xbrjos\Desktop\temp MP\NewDatasets')
for folder in pklsInFolders.keys():
for samplePath in pklsInFolders[folder]:
newSampleResult: SampleResult = results.add_sample(samplePath)
for attr in get_attributes_from_foldername(folder):
newSampleResult.set_attribute(attr)
t0 = time.time()
results.update_all()
print('updating all took', time.time()-t0, 'seconds')
save_results('results1.res', results)
# results: TotalResults = load_results('results1.res')
# results: TotalResults = TotalResults()
# pklsInFolders = get_pkls_from_directory(r'C:\Users\xbrjos\Desktop\temp MP\NewDatasets')
#
# for folder in pklsInFolders.keys():
# for samplePath in pklsInFolders[folder]:
# newSampleResult: SampleResult = results.add_sample(samplePath)
# for attr in get_attributes_from_foldername(folder):
# newSampleResult.set_attribute(attr)
#
# t0 = time.time()
# results.update_all()
# print('updating all took', time.time()-t0, 'seconds')
#
# save_results('results1.res', results)
results: TotalResults = load_results('results1.res')
# save_results('results1.res', results)
plt.clf()
errorPerFraction: dict = results.get_error_vs_fraction_data(attributes=['air', 'water'],
methods=['random', 'sizeBin', 'chemo'])
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()
plot: Figure = get_error_vs_frac_plot(results, attributes=[['air', 'water'], ['sediment', 'soil', 'beach', 'slush']],
methods=[['random', 'size']]*2)
plot.show()
\ No newline at end of file
......@@ -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))
particleContainer.setParticleContours(contours)
return particleContainer
......@@ -258,23 +258,29 @@ class TestSampleResult(unittest.TestCase):
possibleRandomMethods = 2
possibleCrossBoxMethods = 2
possibleSpiralBoxMethods = 3
possibleChemometricMethods = 1
possibleRandomBoxMethods = 3
possibleChemometricMethods = 0
totalPossible = possibleCrossBoxMethods + possibleRandomMethods + \
possibleSpiralBoxMethods + possibleChemometricMethods
possibleSpiralBoxMethods + possibleChemometricMethods + \
possibleRandomBoxMethods
self.assertEqual(len(methods), totalPossible)
self.assertTrue(containsMethod(methods, meth.RandomSampling(dset, desiredFraction)))
self.assertTrue(containsMethod(methods, meth.SizeBinFractioning(dset, desiredFraction)))
self.assertTrue(containsMethod(methods, gmeth.CrossBoxSubSampling(dset, desiredFraction)))
self.assertTrue(containsMethod(methods, gmeth.SpiralBoxSubsampling(dset, desiredFraction)))
self.assertTrue(containsMethod(methods, gmeth.RandomBoxSampling(dset, desiredFraction)))
desiredFraction = 0.5
methods = get_methods_to_test(dset, [desiredFraction])
possibleRandomMethods = 2
possibleCrossBoxMethods = 1
possibleSpiralBoxMethods = 0
possibleChemometricMethods = 1
possibleChemometricMethods = 0
possibleRandomBoxMethods = 0
totalPossible = possibleCrossBoxMethods + possibleRandomMethods + \
possibleSpiralBoxMethods + possibleChemometricMethods
possibleSpiralBoxMethods + possibleChemometricMethods + \
possibleRandomBoxMethods
self.assertEqual(len(methods), totalPossible)
self.assertTrue(containsMethod(methods, meth.RandomSampling(dset, desiredFraction)))
self.assertTrue(containsMethod(methods, meth.SizeBinFractioning(dset, desiredFraction)))
......@@ -286,9 +292,11 @@ class TestSampleResult(unittest.TestCase):
possibleRandomMethods = 2
possibleCrossBoxMethods = 0
possibleSpiralBoxMethods = 0
possibleChemometricMethods = 1
possibleRandomBoxMethods = 0
possibleChemometricMethods = 0
totalPossible = possibleCrossBoxMethods + possibleRandomMethods + \
possibleSpiralBoxMethods + possibleChemometricMethods
possibleSpiralBoxMethods + possibleChemometricMethods + \
possibleRandomBoxMethods
self.assertEqual(len(methods), totalPossible)
self.assertTrue(containsMethod(methods, meth.RandomSampling(dset, desiredFraction)))
self.assertTrue(containsMethod(methods, meth.SizeBinFractioning(dset, desiredFraction)))
......@@ -300,9 +308,11 @@ class TestSampleResult(unittest.TestCase):
possibleRandomMethods = 4
possibleCrossBoxMethods = 3
possibleSpiralBoxMethods = 3
possibleChemometricMethods = 2
possibleChemometricMethods = 0
possibleRandomBoxMethods = 3
totalPossible = possibleCrossBoxMethods + possibleRandomMethods + \
possibleSpiralBoxMethods + possibleChemometricMethods
possibleSpiralBoxMethods + possibleChemometricMethods + \
possibleRandomBoxMethods
self.assertEqual(len(methods), totalPossible)
for desiredFraction in desiredFractions:
self.assertTrue(containsMethod(methods, meth.RandomSampling(dset, desiredFraction)))
......@@ -363,17 +373,25 @@ class TestSubsamplingResult(unittest.TestCase):
self.subsamplingResult.add_result(origParticles, subParticles)
self.assertEqual(len(self.subsamplingResult.mpCountErrors), 1)
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
self.subsamplingResult.add_result(origParticles, subParticles)
self.assertEqual(len(self.subsamplingResult.mpCountErrors), 2)
self.assertEqual(self.subsamplingResult.mpCountErrors[0], 50)
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):
self.subsamplingResult.mpCountErrors = [10, 30, 20]
self.subsamplingResult.estimMPCounts = [2, 5, 3]
self.subsamplingResult.reset_results()
self.assertEqual(self.subsamplingResult.mpCountErrors, [])
self.assertEqual(self.subsamplingResult.estimMPCounts, [])
def test_get_error_per_bin(self):
def get_full_and_sub_particles():
......@@ -450,7 +468,7 @@ class TestSubsamplingResult(unittest.TestCase):
self.subsamplingResult.mpCountErrors = [50, 75, 100]
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.assertEqual(self.subsamplingResult.mpCountError, 75)
......@@ -484,6 +502,7 @@ class TestSubsamplingResult(unittest.TestCase):
return nonMPParticles
def _get_MP_particle(self) -> Particle:
random.seed(15203018)
polymerNames = ['Poly (methyl methacrylate',
'Polyethylene',
'Silicone rubber',
......
import sys
import unittest
import numpy as np
from geometricMethods import BoxSelectionSubsamplingMethod, CrossBoxSubSampling, SpiralBoxSubsampling, BoxSelectionCreator
from geometricMethods import *
sys.path.append("C://Users//xbrjos//Desktop//Python")
from gepard import dataset
class TestBoxSelector(unittest.TestCase):
def setUp(self) -> None:
self.boxSelector: BoxSelectionSubsamplingMethod = BoxSelectionSubsamplingMethod(None)
......@@ -39,6 +37,21 @@ class TestSelectCrossBoxes(unittest.TestCase):
def setUp(self) -> 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):
self.crossBoxSubsampler.filterDiameter = 100
self.crossBoxSubsampler.fraction = 0.1
......@@ -112,22 +125,36 @@ class TestSelectSpiralBoxes(unittest.TestCase):
self.assertEqual(newTopLefts[1], (0, 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
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._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))
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))
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))
def test_boxes_are_overlapping(self):
......@@ -161,6 +188,43 @@ class TestSelectSpiralBoxes(unittest.TestCase):
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):
def setUp(self) -> None:
self.dataset: dataset.DataSet = dataset.DataSet('test')
......@@ -178,6 +242,8 @@ class TestBoxCreator(unittest.TestCase):
desiredFraction: float = 0.1
crossBoxSubsamplers: list = self.boxCreator.get_crossBoxSubsamplers_for_fraction(desiredFraction)
self._assert_correct_partCont(crossBoxSubsamplers)
self.assertEqual(len(crossBoxSubsamplers), 2)
numBoxesAcrossList: list = []
for boxSelector in crossBoxSubsamplers:
......@@ -190,25 +256,55 @@ class TestBoxCreator(unittest.TestCase):
self.assertTrue(5 in numBoxesAcrossList)
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(crossBoxSubsamplers[0].numBoxesAcross, 3)
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)
def test_get_spiralBoxSubsamplers_for_fraction(self):
desiredFraction: float = 0.1
spiralBoxSubsamplers = self.boxCreator.get_spiralBoxSubsamplers_for_fraction(desiredFraction)
self._assert_correct_partCont(spiralBoxSubsamplers)
self.assertTrue(len(spiralBoxSubsamplers) == len(SpiralBoxSubsampling.possibleBoxNumbers)) # all boxNumbers work here
for selector in spiralBoxSubsamplers:
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)
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:
"""
Calculates the size of each box (= width = height), given a certain filter area and number of Boxes
......