...
 
Commits (9)
......@@ -4,3 +4,5 @@
__pycache__/
*.png
*.res
This diff is collapsed.
This diff is collapsed.
......@@ -4,6 +4,7 @@ sys.path.append("C://Users//xbrjos//Desktop//Python")
import gepard
from gepard import dataset
import helpers
import numpy as np
class FilterView(QtWidgets.QGraphicsView):
......@@ -21,8 +22,6 @@ class FilterView(QtWidgets.QGraphicsView):
self.setCacheMode(QtWidgets.QGraphicsView.CacheBackground)
self.setViewportUpdateMode(QtWidgets.QGraphicsView.BoundingRectViewportUpdate)
self.setRenderHint(QtGui.QPainter.Antialiasing)
self.setTransformationAnchor(QtWidgets.QGraphicsView.AnchorUnderMouse)
self.setResizeAnchor(QtWidgets.QGraphicsView.AnchorUnderMouse)
self.drag = None
......@@ -33,28 +32,18 @@ class FilterView(QtWidgets.QGraphicsView):
def update_measure_boxes(self, topLefts: list, boxSize: float) -> None:
self._remove_measure_boxes()
offset = self.filter.circleOffset
for x, y in topLefts:
newBox: MeasureBoxGraphItem = MeasureBoxGraphItem(x+offset[0], y+offset[1], boxSize, boxSize)
newBox: MeasureBoxGraphItem = MeasureBoxGraphItem(x, y, boxSize, boxSize)
self.measuringBoxes.append(newBox)
self.scene().addItem(newBox)
self._update_measured_contours()
def _remove_measure_boxes(self) -> None:
for item in self.measuringBoxes:
self.scene().removeItem(item)
self.measuringBoxes = []
def load_and_update_from_dataset(self, fname: str) -> None:
self.dataset = dataset.loadData(fname)
offset, diameter, widthHeight = helpers.get_filterDimensions_from_dataset(self.dataset)
offsetx = helpers.convert_length_to_pixels(self.dataset, offset[0])
offsety = helpers.convert_length_to_pixels(self.dataset, offset[1])
diameter = helpers.convert_length_to_pixels(self.dataset, diameter)
width = helpers.convert_length_to_pixels(self.dataset, widthHeight[0])
height = helpers.convert_length_to_pixels(self.dataset, widthHeight[1])
self.filter.update_filterSize(width, height, diameter, (offsetx, offsety))
def update_from_dataset(self, dset: dataset.DataSet) -> None:
self.dataset = dset
self._update_particle_contours()
self._fit_to_window()
......@@ -71,21 +60,10 @@ class FilterView(QtWidgets.QGraphicsView):
self.scene().removeItem(cntItem)
self.contourItems = []
def _update_measured_contours(self) -> None:
# offset = self.filter.circleOffset
# offset = (-offset[0], -offset[1])
offset = (0, 0)
for contourItem in self.contourItems:
contourItem.isMeasured = False
for measBox in self.measuringBoxes:
topLeftXY = (measBox.posX, measBox.posY)
boxWidthHeight = (measBox.width, measBox.height)
if helpers.box_overlaps_contour(topLeftXY, boxWidthHeight, contourItem.polygon, offset=offset):
contourItem.isMeasured = True
contourItem.update()
break
def update_measured_particles(self, measuredParticles: list) -> None:
measuredIndices: list = [particle.index for particle in measuredParticles]
for index, contourItem in enumerate(self.contourItems):
contourItem.isMeasured = (index in measuredIndices)
def wheelEvent(self, event: QtGui.QWheelEvent) -> None:
factor: float = 1.01 ** (event.angleDelta().y() / 8)
......
from PyQt5 import QtWidgets
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
import helpers
class MainView(QtWidgets.QWidget):
......@@ -64,8 +69,27 @@ class MainView(QtWidgets.QWidget):
def _load_dataset(self) -> None:
fname = QtWidgets.QFileDialog.getOpenFileName(self, 'Select .pkl file', filter='pkl file (*.pkl)')
if fname[0] != '':
self.filterView.load_and_update_from_dataset(fname[0])
self.activeMode.update_measure_viewItems()
try:
dset: dataset.DataSet = dataset.loadData(fname[0])
except IndexError:
dset = None
QtWidgets.QMessageBox.critical(self, 'Index Error', 'Unable to load dataset..')
if dset is not None:
offset, diameter, widthHeight = helpers.get_filterDimensions_from_dataset(dset)
offsetx = helpers.convert_length_to_pixels(dset, offset[0])
offsety = helpers.convert_length_to_pixels(dset, offset[1])
diameter = helpers.convert_length_to_pixels(dset, diameter)
width = helpers.convert_length_to_pixels(dset, widthHeight[0])
height = helpers.convert_length_to_pixels(dset, widthHeight[1])
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)
self.filterView.update_from_dataset(dset)
self.activeMode.update_measure_viewItems()
if __name__ == '__main__':
......
from PyQt5 import QtCore, QtWidgets
from gui.filterView import FilterView, MeasureBoxGraphItem
from geometricMethods import CrossBoxSelector, SpiralSelector
from geometricMethods import BoxSelectionSubsamplingMethod, CrossBoxSubSampling, SpiralBoxSubsampling
class MeasureMode(QtCore.QObject):
def __init__(self, relatedFilterView: FilterView):
super(MeasureMode, self).__init__()
self.filterView = relatedFilterView
self.filterView: FilterView = relatedFilterView
self.uiControls: QtWidgets.QGroupBox = QtWidgets.QGroupBox()
self.boxGenerator: BoxSelectionSubsamplingMethod = None
def get_control_groupBox(self) -> QtWidgets.QGroupBox:
return self.uiControls
......@@ -15,29 +16,35 @@ class MeasureMode(QtCore.QObject):
def update_measure_viewItems(self) -> None:
raise NotImplementedError
def _send_measuredParticles_to_filterview(self) -> None:
if self.boxGenerator.particleContainer is not None:
subParticles = self.boxGenerator.apply_subsampling_method()
self.filterView.update_measured_particles(subParticles)
class CrossBoxMode(MeasureMode):
def __init__(self, *args):
super(CrossBoxMode, self).__init__(*args)
self.uiControls = CrossBoxesControls(self)
self.crossBoxGenerator: CrossBoxSelector = CrossBoxSelector(None)
self.boxGenerator: CrossBoxSubSampling = CrossBoxSubSampling(None)
self.update_measure_viewItems()
def update_measure_viewItems(self) -> None:
self.crossBoxGenerator.filterDiameter = self.filterView.filter.diameter
self.crossBoxGenerator.numBoxesAcross = int(self.uiControls.numBoxesSelector.currentText())
self.boxGenerator.filterDiameter = self.filterView.filter.diameter
self.boxGenerator.numBoxesAcross = int(self.uiControls.numBoxesSelector.currentText())
desiredCoverage: int = self.uiControls.coverageSpinbox.value()
maxCoverage: int = int(self.crossBoxGenerator.get_maximum_achievable_fraction() * 100)
maxCoverage: int = int(self.boxGenerator.get_maximum_achievable_fraction() * 100)
self.uiControls.set_to_max_possible_coverage(maxCoverage)
if desiredCoverage > maxCoverage:
desiredCoverage = maxCoverage
self.crossBoxGenerator.fraction = desiredCoverage / 100
self.boxGenerator.fraction = desiredCoverage / 100
topLefts: list = self.crossBoxGenerator.get_topLeft_of_boxes()
boxSize = self.crossBoxGenerator.boxSize
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 CrossBoxesControls(QtWidgets.QGroupBox):
......@@ -84,23 +91,19 @@ class SpiralBoxMode(MeasureMode):
def __init__(self, *args):
super(SpiralBoxMode, self).__init__(*args)
self.uiControls: SpiralBoxControls = SpiralBoxControls(self)
self.spiralBoxGenerator: SpiralSelector = SpiralSelector(None)
self.boxGenerator: SpiralBoxSubsampling = SpiralBoxSubsampling(None)
self.update_measure_viewItems()
def update_measure_viewItems(self) -> None:
self.spiralBoxGenerator.filterDiameter = self.filterView.filter.diameter
self.spiralBoxGenerator.boxSize = self.uiControls.boxSizeSpinbox.value()
# minBoxSize: float = self.spiralBoxGenerator.filterDiameter*0.1
# if self.spiralBoxGenerator.boxSize < minBoxSize:
# self.spiralBoxGenerator.boxSize = minBoxSize
# self.uiControls.boxSizeSpinbox.setValue(int(round(minBoxSize)))
self.boxGenerator.filterDiameter = self.filterView.filter.diameter
self.spiralBoxGenerator.numBoxes = self.uiControls.numBoxesSpinbox.value()
self.boxGenerator.numBoxes = self.uiControls.numBoxesSpinbox.value()
self.boxGenerator.fraction = self.uiControls.coverageSpinbox.value() / 100
topLefts: list = self.spiralBoxGenerator.get_topLeft_of_boxes()
boxSize = self.spiralBoxGenerator.boxSize
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):
......@@ -115,20 +118,23 @@ class SpiralBoxControls(QtWidgets.QGroupBox):
layout = QtWidgets.QHBoxLayout()
self.setLayout(layout)
layout.addWidget(QtWidgets.QLabel('Box Size:'))
self.boxSizeSpinbox = QtWidgets.QSpinBox()
self.boxSizeSpinbox.setValue(50)
self.boxSizeSpinbox.setMaximum(10000)
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)
layout.addStretch()
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):
self.measureModeParent.update_measure_viewItems()
if self.numBoxesSpinbox.value() > 0:
self.measureModeParent.update_measure_viewItems()
......@@ -32,39 +32,47 @@ class ParticleBinSorter(object):
return binIndex
def box_overlaps_contour(boxTopLeftXY: tuple, boxWidthHeight: tuple, contour, offset: tuple = (0, 0)) -> bool:
def get_Anger_fraction(numParticles, sigma=1.65, mpFraction=0.01, errorMargin=0.1):
"""
Returns the required fraction for reaching the defined errorMargin at a given number of Particles.
According to: Anger et al. "Raman microspectroscopy as a tool for microplastic particle analysis",
TrAC Trends in Analytical Chemistry, 2018, 214-226. (https://doi.org/10.1016/j.trac.2018.10.010)
:param numParticles:
:param sigma:
:param mpFraction:
:param errorMargin:
:return:
"""
N = numParticles
P = mpFraction
e = P * errorMargin
numParticlesMeasured = np.ceil(P * (1 - P) / (e**2 / sigma**2 + P*(1 - P) / N))
return np.int(numParticlesMeasured)
def box_overlaps_contour(boxTopLeftXY: tuple, boxWidthHeight: tuple, contourData: np.ndarray) -> bool:
"""
Calculates, if a contour is overlapping a box.
:param boxTopLeftXY: topLeft of Box
:param boxWidthHeight: Width and height of box
:param contour: np.ndarrayof contour data
:param offset: optional offset (x, y) of the box (i.e., the (0, 0) of the contours coord system does not match
the (0, 0) of the box coord system.
:param contourData: np.ndarrayof contour data
:return:
"""
contourPolygon = QtGui.QPolygonF()
if type(contour) == np.ndarray:
for point in contour:
contourPolygon.append(QtCore.QPointF(point[0, 0], point[0, 1]))
elif type(contour) == QtGui.QPolygonF:
contourPolygon = contour
else:
raise TypeError
boxPolygon = QtGui.QPolygonF()
boxPolygon.append(QtCore.QPointF(boxTopLeftXY[0]+offset[0], boxTopLeftXY[1]+offset[1]))
boxPolygon.append(QtCore.QPointF(boxTopLeftXY[0]+offset[0], boxTopLeftXY[1] + boxWidthHeight[1]+offset[1]))
boxPolygon.append(QtCore.QPointF(boxTopLeftXY[0]+offset[0] + boxWidthHeight[0], boxTopLeftXY[1]+offset[1]))
boxPolygon.append(QtCore.QPointF(boxTopLeftXY[0]+offset[0] + boxWidthHeight[0],
boxTopLeftXY[1] + boxWidthHeight[1]+offset[1]))
isOverlapping: bool = boxPolygon.intersects(contourPolygon)
if not isOverlapping:
# sometimes, the polygon.intersects method does not capture everything... We test the brects therefore..
polygonBrect: QtCore.QRectF = contourPolygon.boundingRect()
boxBrect: QtCore.QRectF = boxPolygon.boundingRect()
if boxBrect.contains(polygonBrect) or boxBrect.intersects(polygonBrect):
isOverlapping = True
isOverlapping: bool = False
xmin, xmax = np.min(contourData[:, 0, 0]), np.max(contourData[:, 0, 0])
width: float = xmax - xmin
boxXmin, boxXmax = boxTopLeftXY[0], boxTopLeftXY[0] + boxWidthHeight[0]
if xmin > (boxXmin-width/2):
if xmax < (boxXmax+width/2):
ymin, ymax = np.min(contourData[:, 0, 1]), np.max(contourData[:, 0, 1])
height = ymax - ymin
boxYmin, boxYmax = boxTopLeftXY[1], boxTopLeftXY[1] + boxWidthHeight[1]
if ymin > (boxYmin-height/2):
if ymax < (boxYmax+width/2):
isOverlapping = True
return isOverlapping
......
import os
import pickle
from evaluation import TotalResults
def load_results(fname: str) -> TotalResults:
res: TotalResults = None
if os.path.exists(fname):
with open(fname, "rb") as fp:
res = pickle.load(fp)
return res
return None
def save_results(fname: str, result: TotalResults) -> None:
with open(fname, "wb") as fp:
pickle.dump(result, fp, protocol=-1)
def get_pkls_from_directory(dirPath: str) -> dict:
"""
Takes a directory and finds all pkl files in there. The result is returned in a dictionary, where
each subfolder is present as a key and the actual pkl paths in a list as values.
:param dirPath:
:return:
"""
resultDict: dict = {}
subFolders = [x[1] for x in os.walk(dirPath)][0]
for subFolder in subFolders:
if subFolder.find('ignore') == -1:
subFolderPath: str = os.path.join(dirPath, subFolder)
filesInFolder: list = os.listdir(subFolderPath)
pkls: list = [os.path.join(subFolderPath, file) for file in filesInFolder if file.endswith('.pkl')]
resultDict[subFolder] = pkls
return resultDict
def get_attributes_from_foldername(foldername: str) -> list:
return [name.strip() for name in foldername.split(',')]
\ No newline at end of file
......@@ -16,60 +16,90 @@ class SubsamplingMethod(object):
self.particleContainer = particleConatainer
self.fraction = desiredFraction
def apply_subsampling_method(self) -> tuple:
@property
def label(self) -> str:
"""
A specific label that can be used for plots, for instance.
:return:
"""
raise NotImplementedError
def apply_subsampling_method(self) -> list:
"""
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:
:returns listOfActuallyMeasuredParticles:
"""
raise NotImplementedError
def equals(self, otherMethod) -> bool:
"""
Checks if another provided method has the same configuration as the used instance.
:param otherMethod:
:return isEqual:
"""
raise NotImplementedError
def matches_any_pattern(self, patternList: list) -> bool:
"""
Tests. wether one of the given patterns is matching.
:param patternList:
:return:
"""
matches: bool = False
for pattern in patternList:
if self.matches_pattern(pattern):
matches = True
break
return matches
def matches_pattern(self, pattern: str) -> bool:
"""
Tests, wether the method matches a given pattern. Strings of at least 4 characters are required!
:param pattern: The string to test against
:return matchesThePattern:
"""
matches: bool = False
if len(pattern) > 3 and not pattern == 'layout':
matches = (self.label.lower().find(pattern.lower()) != -1)
return matches
class RandomSampling(SubsamplingMethod):
def apply_subsampling_method(self):
@property
def label(self) -> str:
return 'Random Subsampling'
def apply_subsampling_method(self) -> list:
numOrigParticles = len(self.particleContainer.particles)
numParticles = self._get_number_of_random_particles(numOrigParticles)
subParticles = random.sample(self.particleContainer.particles, numParticles)
return self.fraction, subParticles
return subParticles
def _get_number_of_random_particles(self, numTotalParticles):
return np.int(np.ceil(numTotalParticles * self.fraction))
class IvlevaSubsampling(SubsamplingMethod):
def __init__(self, particleContainer, sigma=1.65, mpFraction=0.01, errorMargin=0.1):
super(IvlevaSubsampling, self).__init__(particleContainer)
self.sigma = sigma
self.estimatedMPFraction = mpFraction
self.errorMargin = errorMargin
def apply_subsampling_method(self):
N = self.particleContainer.getNumberOfParticles()
numParticlesMeasured = self._get_ivleva_fraction(N)
subParticles = random.sample(self.particleContainer.particles, numParticlesMeasured)
fractionMeasured = numParticlesMeasured/N
return fractionMeasured, subParticles
def _get_ivleva_fraction(self, N):
P = self.estimatedMPFraction
e = P * self.errorMargin
numParticlesMeasured = np.ceil(P*(1 - P) / (e**2/self.sigma**2 + P*(1-P)/N))
return np.int(numParticlesMeasured)
def equals(self, otherMethod) -> bool:
return type(otherMethod) == type(self) and otherMethod.fraction == self.fraction
class SizeBinFractioning(SubsamplingMethod):
def __init__(self, particleConatiner, desiredfraction: float = 0.2):
super(SizeBinFractioning, self).__init__(particleConatiner, desiredfraction)
self.sorter: ParticleBinSorter = ParticleBinSorter()
def apply_subsampling_method(self):
@property
def label(self) -> str:
return 'SizeBin Subsampling'
def apply_subsampling_method(self) -> list:
subParticlesPerBin: list = self._get_subParticles_per_bin(self.particleContainer.particles)
subParticles: list = []
for subParticleList in subParticlesPerBin:
for particle in subParticleList:
subParticles.append(particle)
return self.fraction, subParticles
return subParticles
def _get_subParticles_per_bin(self, particleList: list):
particlesInBins: list = self.sorter.sort_particles_into_bins(particleList)
......@@ -84,3 +114,6 @@ class SizeBinFractioning(SubsamplingMethod):
subParticlesPerBin.append(subParticlesInBin)
return subParticlesPerBin
def equals(self, otherMethod) -> bool:
return type(otherMethod) == type(self) and otherMethod.fraction == self.fraction
import numpy as np
import matplotlib.pyplot as plt
import time
import sys
sys.path.append("C://Users//xbrjos//Desktop//Python")
from gepard import dataset
from methods import IvlevaSubsampling, RandomSampling, SizeBinFractioning
from geometricMethods import BoxSelectionCreator
from helpers import ParticleBinSorter
from evaluation import ResultComparer
from evaluation import TotalResults, SampleResult
from input_output import get_pkls_from_directory, get_attributes_from_foldername, save_results, load_results
fname: str = r'C:\Users\xbrjos\Desktop\temp MP\190313_Soil_5_A_50_5_1_50_1\190313_Soil_5_A_50_5_1_50_1.pkl'
# fname: str = r'C:\Users\xbrjos\Desktop\temp MP\190326_MCII_WWTP_SB_50_2\190326_MCII_WWTP_SB_50_2.pkl'
# fname: str = r'C:\Users\xbrjos\Desktop\temp MP\190326_MCII_WWTP_SB_50_1\190326_MCII_WWTP_SB_50_1.pkl'
# fname: str = r'C:\Users\xbrjos\Desktop\temp MP\KWS_CT_3_ds1_all_10_2\KWS_CT_3_ds1_all_10_2.pkl' #legacy convert not working..
# fname: str = r'C:\Users\xbrjos\Desktop\temp MP\190201_BSB_Stroomi_ds2_R1_R2_50\190201_BSB_Stroomi_ds2_R1_R2_50.pkl' #zvalues image missing, legacy convert fails..
"""
IMPORTANT!!!
SET GEPARD TO EVALUATION BRANCH (WITHOUT THE TILING STUFF), OTHERWISE SOME OF THE LEGACY CONVERTS MIGHT FAIL..
"""
dset = dataset.loadData(fname)
print('loaded dataset')
boxCreator = BoxSelectionCreator(dset)
center, size = boxCreator.get_filterDimensions_from_dataset()
print(center, size)
print(dset.mapToPixel(center, force=True))
# pc = dset.particleContainer
# origParticles = pc.particles
# resultComparer = ResultComparer()
# numOrigMP = resultComparer._get_number_of_MP_particles(origParticles)
# print(f'orig particles: {len(origParticles)}, of which are mp: {numOrigMP}')
# # ivlevaSampling = IvlevaSubsampling(pc)
# # ivlevaFraction, ivlevaParticles = ivlevaSampling.apply_subsampling_method()
# 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()
# fractions = np.arange(0.05, .55, 0.05)
# errors = []
# binErrors = []
# numIterations = 1000
# for fraction in fractions:
# print('random sampling, fraction:', fraction)
# # randomSampling = RandomSampling(pc, desiredFraction=fraction)
# randomSampling = SizeBinFractioning(pc, fraction)
# iterErrors = []
# binIterErrors = []
# for _ in range(numIterations):
# randomFraction, randomParticles = randomSampling.apply_subsampling_method()
# iterErrors.append(resultComparer._get_mp_count_error(origParticles, randomParticles, randomFraction))
# bins, errorsPerBin = resultComparer._get_mp_count_error_per_bin(origParticles, randomParticles, randomFraction)
# binIterErrors.append(errorsPerBin)
# errors.append(round(np.mean(iterErrors)*100)) #from fraction to %
# fractionBinErrors = []
# for binIndex in range(len(bins)+1):
# binError = round(np.mean([binIterErrors[i][binIndex] for i in range(numIterations)]) * 100)
# fractionBinErrors.append(binError)
# binErrors.append(fractionBinErrors)
# print('random sampling took', np.round(time.time()-t0, 2), 'seonds')
# binLowerLimits = bins.copy()
# binLowerLimits.insert(0, 0)
# plt.subplot(121)
# plt.plot(fractions, errors)
# # plt.title(f'Random Sampling, averaged from {numIterations} trials, orig particle count: {len(origParticles)}')
# plt.xlabel('Fraction measured')
# plt.ylabel('Average error in MP particle count (%)')
# plt.subplot(122)
# for fracMeas, curBinErrors in zip(fractions, binErrors):
# plt.plot(binLowerLimits, curBinErrors, label=np.round(fracMeas, 1))
# # plt.title('Error in MP count (%) per size bin')
# plt.xlabel('particle size')
# plt.ylabel('Average error in MP particle count (%)')
# plt.legend()
# plt.show()
# results.update_all()
# print('updating all took', time.time()-t0, 'seconds')
#
# save_results('results1.res', results)
results: TotalResults = load_results('results1.res')
errorPerFraction: dict = results.get_error_vs_fraction_data(methods=['spiral', 'cross'])
plt.clf()
for methodLabel in errorPerFraction.keys():
fractions: list = list(errorPerFraction[methodLabel].keys())
errors: list = list(errorPerFraction[methodLabel].values())
plt.plot(fractions, errors, label=methodLabel)
plt.title('Spiral or Box Layouts')
plt.xscale('log')
plt.xlabel('measured fraction')
plt.ylabel('mpCountError')
plt.legend()
plt.show()
# # sizeBinSampling = SizeBinFractioning(pc)
# # sizeBinParticles = sizeBinSampling.apply_subsampling_method()
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
......@@ -68,6 +68,25 @@ class TestBinSorter(unittest.TestCase):
class TestOther(unittest.TestCase):
def test_get_Anger_fraction(self):
numParticles = helpers.get_Anger_fraction(1E6, sigma=1.65, mpFraction=0.05, errorMargin=0.1)
self.assertEqual(numParticles, 5147)
numParticles = helpers.get_Anger_fraction(1E6, sigma=1.65, mpFraction=0.005, errorMargin=0.1)
self.assertEqual(numParticles, 51394)
numParticles = helpers.get_Anger_fraction(1E6, sigma=1.65, mpFraction=0.0005, errorMargin=0.1)
self.assertEqual(numParticles, 352428)
numParticles = helpers.get_Anger_fraction(1E6, sigma=1.65, mpFraction=0.05, errorMargin=0.2)
self.assertEqual(numParticles, 1292)
numParticles = helpers.get_Anger_fraction(1E6, sigma=1.65, mpFraction=0.005, errorMargin=0.3)
self.assertEqual(numParticles, 5984)
numParticles = helpers.get_Anger_fraction(1E6, sigma=1.65, mpFraction=0.0005, errorMargin=0.3)
self.assertEqual(numParticles, 57022)
def test_box_overlaps_contour(self):
boxXY: tuple = 0, 0
boxWidthHeight: tuple = 10, 10
......@@ -78,15 +97,14 @@ class TestOther(unittest.TestCase):
contourPoints = np.array([[[1, 1]], [[5, 5]], [[3, 3]]]) # fully enclosed
self.assertTrue(helpers.box_overlaps_contour(boxXY, boxWidthHeight, contourPoints))
contourPoints = np.array([[[-5, -5]], [[0, 5]], [[-5, -10]]]) # only one point touches border
contourPoints = np.array([[[-5, -5]], [[-1, 5]], [[-5, -10]]]) # outside the box
self.assertFalse(helpers.box_overlaps_contour(boxXY, boxWidthHeight, contourPoints))
contourPoints = np.array([[[-2.5, 0]], [[5, 0]], [[5, 5]], [[-2.5, 5]]]) # inside more than 50 %
self.assertTrue(helpers.box_overlaps_contour(boxXY, boxWidthHeight, contourPoints))
offset: tuple = (1, 0) # now it does not touch it anymore
self.assertFalse(helpers.box_overlaps_contour(boxXY, boxWidthHeight, contourPoints, offset))
contourPoints = np.array([[[-5, -5]], [[-1, 5]], [[-5, -10]]]) # outside the box
contourPoints = np.array([[[-2.5, 0]], [[2, 0]], [[2, 2]], [[-2.5, 2]]]) # inside less than 50 %
self.assertFalse(helpers.box_overlaps_contour(boxXY, boxWidthHeight, contourPoints))
offset = (-5, -5) # now it overlaps
self.assertTrue(helpers.box_overlaps_contour(boxXY, boxWidthHeight, contourPoints, offset))
def test_get_overlapping_fraction(self):
polygon1: QtGui.QPolygonF = QtGui.QPolygonF()
......
import unittest
import os
import shutil
from input_output import get_pkls_from_directory, get_attributes_from_foldername, load_results, save_results
from evaluation import TotalResults
class TestIO(unittest.TestCase):
folders: list = ['air', 'water', 'sediment', 'ignore']
samples: list = ['sample1', 'sample2', 'sample3']
extensions: list = ['.pkl', '.txt', '.xlsx']
def setUp(self) -> None:
self.path: str = os.getcwd()
self.ignoreFile: str = os.path.join(self.path, 'ignored.pkl')
with open(self.ignoreFile, 'w') as fp:
fp.write('empty')
for folder in self.folders:
folderPath: str = os.path.join(self.path, folder)
os.mkdir(folderPath)
for sample in self.samples:
for ext in self.extensions:
fname: str = os.path.join(folderPath, sample+ext)
with open(fname, 'w') as fp:
fp.write('empty')
def tearDown(self) -> None:
os.remove(self.ignoreFile)
for folder in self.folders:
folderPath: str = os.path.join(self.path, folder)
shutil.rmtree(folderPath)
def test_load_save(self):
newRes: TotalResults = TotalResults()
fname: str = os.path.join(self.path, 'test.res')
save_results(fname, newRes)
self.assertTrue(os.path.exists(fname))
loadedRes: TotalResults = load_results(fname)
self.assertTrue(loadedRes is not None)
self.assertEqual(type(loadedRes), TotalResults)
def test_read_pkls_from_dir(self):
pklsInFolder: dict = get_pkls_from_directory(self.path)
# the ignore folder is to be skipped
self.assertEqual(len(pklsInFolder.keys()), len(self.folders)-1)
ignoredFileFound: bool = False
wrongFileTypeFound: bool = False
ignoredFolderFound: bool = False
for folder in pklsInFolder.keys():
self.assertTrue(folder in self.folders)
self.assertEqual(len(pklsInFolder[folder]), len(self.samples))
if folder.find('ignored') != -1:
ignoredFolderFound = True
for samplePath in pklsInFolder[folder]:
self.assertTrue(os.path.exists(samplePath))
basename: str = os.path.basename(samplePath)
samplename, extension = basename.split('.')
self.assertTrue(samplename in self.samples)
self.assertTrue(basename.endswith('.pkl'))
if samplename.find('ignored') != -1:
ignoredFileFound = True
if extension != 'pkl':
wrongFileTypeFound = True
self.assertFalse(ignoredFolderFound)
self.assertFalse(ignoredFileFound)
self.assertFalse(wrongFileTypeFound)
def test_get_attributes_from_foldername(self):
folderName: str = 'Slush'
attributes: list = get_attributes_from_foldername(folderName)
self.assertEqual(attributes, ['Slush'])
folderName: str = 'Sediment, Strand'
attributes: list = get_attributes_from_foldername(folderName)
self.assertEqual(attributes, ['Sediment', 'Strand'])
folderName = 'Water, Wasser, Liquid'
attributes = get_attributes_from_foldername(folderName)
self.assertEqual(attributes, ['Water', 'Wasser', 'Liquid'])
......@@ -13,7 +13,8 @@ import numpy as np
import gepard
from gepard.analysis.particleContainer import ParticleContainer
from gepard.analysis.particleAndMeasurement import Particle
from methods import IvlevaSubsampling, RandomSampling, SizeBinFractioning
from methods import RandomSampling, SizeBinFractioning
import geometricMethods as gmeth
from helpers import ParticleBinSorter
......@@ -23,28 +24,6 @@ def get_default_particle_container(numParticles=1000):
return particleContainer
class TestIvleva(unittest.TestCase):
def test_get_ivleva_fraction(self):
self.particleContainer = get_default_particle_container()
ivlevaSampling = IvlevaSubsampling(self.particleContainer, sigma=1.65, mpFraction=0.05, errorMargin=0.1)
numParticles = ivlevaSampling._get_ivleva_fraction(1E6)
self.assertEqual(numParticles, 5147)
ivlevaSampling = IvlevaSubsampling(self.particleContainer, sigma=1.65, mpFraction=0.005, errorMargin=0.1)
numParticles = ivlevaSampling._get_ivleva_fraction(1E6)
self.assertEqual(numParticles, 51394)
ivlevaSampling = IvlevaSubsampling(self.particleContainer, sigma=1.65, mpFraction=0.0005, errorMargin=0.1)
numParticles = ivlevaSampling._get_ivleva_fraction(1E6)
self.assertEqual(numParticles, 352428)
ivlevaSampling = IvlevaSubsampling(self.particleContainer, sigma=1.65, mpFraction=0.05, errorMargin=0.1)
numParticles = ivlevaSampling._get_ivleva_fraction(1000)
ivlevaFraction, ivlevaParticles = ivlevaSampling.apply_subsampling_method()
self.assertEqual(len(ivlevaParticles), numParticles)
class TestRandomParticles(unittest.TestCase):
def test_get_number_of_random_particles(self):
randomSampling = RandomSampling(None, desiredFraction=0.1)
......@@ -87,5 +66,82 @@ class TestSizeBinFractioning(unittest.TestCase):
self.assertEqual(len(subParticles), numParticlesPerBinExpected)
if __name__ == '__main__':
unittest.main()
class TestMethodEquality(unittest.TestCase):
def test_methodEquality_and_patterns(self):
method1_1: RandomSampling = RandomSampling(None, 0.1)
method1_2: RandomSampling = RandomSampling(None, 0.2)
method2_1: SizeBinFractioning = SizeBinFractioning(None, 0.1)
method2_2: SizeBinFractioning = SizeBinFractioning(None, 0.2)
method3_1_1: gmeth.CrossBoxSubSampling = gmeth.CrossBoxSubSampling(None, 0.1)
method3_1_1.numBoxesAcross = 3
method3_1_2: gmeth.CrossBoxSubSampling = gmeth.CrossBoxSubSampling(None, 0.1)
method3_1_2.numBoxesAcross = 5
method3_2_1: gmeth.CrossBoxSubSampling = gmeth.CrossBoxSubSampling(None, 0.2)
method3_2_1.numBoxesAcross = 3
method3_2_2: gmeth.CrossBoxSubSampling = gmeth.CrossBoxSubSampling(None, 0.2)
method3_2_2.numBoxesAcross = 5
method4_1_1: gmeth.SpiralBoxSubsampling = gmeth.SpiralBoxSubsampling(None, 0.1)
method4_1_1.numBoxes = 5
method4_1_2: gmeth.SpiralBoxSubsampling = gmeth.SpiralBoxSubsampling(None, 0.1)
method4_1_2.numBoxes = 10
method4_2_1: gmeth.SpiralBoxSubsampling = gmeth.SpiralBoxSubsampling(None, 0.2)
method4_2_1.numBoxes = 5
method4_2_2: gmeth.SpiralBoxSubsampling = gmeth.SpiralBoxSubsampling(None, 0.2)
method4_2_2.numBoxes = 10
methods = [method1_1, method1_2, method2_1, method2_2, method3_1_1, method3_1_2,
method3_2_1, method3_2_2, method4_1_1, method4_1_2, method4_2_1, method4_2_2]
for index1, method1 in enumerate(methods):
for index2, method2 in enumerate(methods):
if index1 == index2:
self.assertTrue(method1.equals(method2))
else:
self.assertFalse(method1.equals(method2))
randomPatterns: list = ['random', 'ranDOm']
sizeBinPatterns: list = ['size', 'Size', 'sizeBin']
crossBoxPatterns: list = ['cross', 'crossLayout']
spiralBoxPatterns: list = ['spiral', 'spiralLayout']
antiPatterns: list = ['bin', 'box', 'crossBox', 'layout'] # pattern 'layout' is ambiguous...
for randomMeth in [method1_1, method1_2]:
for pos in randomPatterns:
self.assertTrue(randomMeth.matches_pattern(pos))
negPatterns = sizeBinPatterns + crossBoxPatterns + spiralBoxPatterns + antiPatterns
for neg in negPatterns:
self.assertFalse(randomMeth.matches_pattern(neg))
self.assertTrue(randomMeth.matches_any_pattern(randomPatterns + negPatterns))
self.assertFalse(randomMeth.matches_any_pattern(negPatterns))
for sizeBinMeth in [method2_1, method2_2]:
for pos in sizeBinPatterns:
self.assertTrue(sizeBinMeth.matches_pattern(pos))
negPatterns = randomPatterns + crossBoxPatterns + spiralBoxPatterns + antiPatterns
for neg in negPatterns:
self.assertFalse(sizeBinMeth.matches_pattern(neg))
self.assertTrue(sizeBinMeth.matches_any_pattern(sizeBinPatterns + negPatterns))
self.assertFalse(sizeBinMeth.matches_any_pattern(negPatterns))
for crossBoxMethod in [method3_1_1, method3_1_2, method3_2_1, method3_2_2]:
for pos in crossBoxPatterns:
self.assertTrue(crossBoxMethod.matches_pattern(pos))
negPatterns = randomPatterns + sizeBinPatterns + spiralBoxPatterns + antiPatterns
for neg in negPatterns:
self.assertFalse(crossBoxMethod.matches_pattern(neg))
self.assertTrue(crossBoxMethod.matches_any_pattern(crossBoxPatterns + negPatterns))
self.assertFalse(crossBoxMethod.matches_any_pattern(negPatterns))
for spiralBoxMethod in [method4_1_1, method4_1_2, method4_2_1, method4_2_2]:
for pos in spiralBoxPatterns:
self.assertTrue(spiralBoxMethod.matches_pattern(pos))
negPatterns = randomPatterns + sizeBinPatterns + crossBoxPatterns + antiPatterns
for neg in negPatterns:
self.assertFalse(spiralBoxMethod.matches_pattern(neg))
self.assertTrue(spiralBoxMethod.matches_any_pattern(spiralBoxPatterns + negPatterns))
self.assertFalse(spiralBoxMethod.matches_any_pattern(negPatterns))