...
 
Commits (9)
...@@ -4,3 +4,5 @@ ...@@ -4,3 +4,5 @@
__pycache__/ __pycache__/
*.png *.png
*.res
This diff is collapsed.
This diff is collapsed.
...@@ -4,6 +4,7 @@ sys.path.append("C://Users//xbrjos//Desktop//Python") ...@@ -4,6 +4,7 @@ sys.path.append("C://Users//xbrjos//Desktop//Python")
import gepard import gepard
from gepard import dataset from gepard import dataset
import helpers import helpers
import numpy as np
class FilterView(QtWidgets.QGraphicsView): class FilterView(QtWidgets.QGraphicsView):
...@@ -21,8 +22,6 @@ class FilterView(QtWidgets.QGraphicsView): ...@@ -21,8 +22,6 @@ class FilterView(QtWidgets.QGraphicsView):
self.setCacheMode(QtWidgets.QGraphicsView.CacheBackground) self.setCacheMode(QtWidgets.QGraphicsView.CacheBackground)
self.setViewportUpdateMode(QtWidgets.QGraphicsView.BoundingRectViewportUpdate) self.setViewportUpdateMode(QtWidgets.QGraphicsView.BoundingRectViewportUpdate)
self.setRenderHint(QtGui.QPainter.Antialiasing) self.setRenderHint(QtGui.QPainter.Antialiasing)
self.setTransformationAnchor(QtWidgets.QGraphicsView.AnchorUnderMouse)
self.setResizeAnchor(QtWidgets.QGraphicsView.AnchorUnderMouse)
self.drag = None self.drag = None
...@@ -33,28 +32,18 @@ class FilterView(QtWidgets.QGraphicsView): ...@@ -33,28 +32,18 @@ class FilterView(QtWidgets.QGraphicsView):
def update_measure_boxes(self, topLefts: list, boxSize: float) -> None: def update_measure_boxes(self, topLefts: list, boxSize: float) -> None:
self._remove_measure_boxes() self._remove_measure_boxes()
offset = self.filter.circleOffset
for x, y in topLefts: 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.measuringBoxes.append(newBox)
self.scene().addItem(newBox) self.scene().addItem(newBox)
self._update_measured_contours()
def _remove_measure_boxes(self) -> None: def _remove_measure_boxes(self) -> None:
for item in self.measuringBoxes: for item in self.measuringBoxes:
self.scene().removeItem(item) self.scene().removeItem(item)
self.measuringBoxes = [] self.measuringBoxes = []
def load_and_update_from_dataset(self, fname: str) -> None: def update_from_dataset(self, dset: dataset.DataSet) -> None:
self.dataset = dataset.loadData(fname) self.dataset = dset
offset, diameter, widthHeight = helpers.get_filterDimensions_from_dataset(self.dataset)
offsetx = helpers.convert_length_to_pixels(self.dataset, offset[0])
offsety = helpers.convert_length_to_pixels(self.dataset, offset[1])
diameter = helpers.convert_length_to_pixels(self.dataset, diameter)
width = helpers.convert_length_to_pixels(self.dataset, widthHeight[0])
height = helpers.convert_length_to_pixels(self.dataset, widthHeight[1])
self.filter.update_filterSize(width, height, diameter, (offsetx, offsety))
self._update_particle_contours() self._update_particle_contours()
self._fit_to_window() self._fit_to_window()
...@@ -71,21 +60,10 @@ class FilterView(QtWidgets.QGraphicsView): ...@@ -71,21 +60,10 @@ class FilterView(QtWidgets.QGraphicsView):
self.scene().removeItem(cntItem) self.scene().removeItem(cntItem)
self.contourItems = [] self.contourItems = []
def _update_measured_contours(self) -> None: def update_measured_particles(self, measuredParticles: list) -> None:
measuredIndices: list = [particle.index for particle in measuredParticles]
# offset = self.filter.circleOffset for index, contourItem in enumerate(self.contourItems):
# offset = (-offset[0], -offset[1]) contourItem.isMeasured = (index in measuredIndices)
offset = (0, 0)
for contourItem in self.contourItems:
contourItem.isMeasured = False
for measBox in self.measuringBoxes:
topLeftXY = (measBox.posX, measBox.posY)
boxWidthHeight = (measBox.width, measBox.height)
if helpers.box_overlaps_contour(topLeftXY, boxWidthHeight, contourItem.polygon, offset=offset):
contourItem.isMeasured = True
contourItem.update()
break
def wheelEvent(self, event: QtGui.QWheelEvent) -> None: def wheelEvent(self, event: QtGui.QWheelEvent) -> None:
factor: float = 1.01 ** (event.angleDelta().y() / 8) factor: float = 1.01 ** (event.angleDelta().y() / 8)
......
from PyQt5 import QtWidgets 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.filterView import FilterView
from gui.measureModes import MeasureMode, CrossBoxMode, CrossBoxesControls, SpiralBoxMode from gui.measureModes import MeasureMode, CrossBoxMode, CrossBoxesControls, SpiralBoxMode
import helpers
class MainView(QtWidgets.QWidget): class MainView(QtWidgets.QWidget):
...@@ -64,8 +69,27 @@ class MainView(QtWidgets.QWidget): ...@@ -64,8 +69,27 @@ class MainView(QtWidgets.QWidget):
def _load_dataset(self) -> None: def _load_dataset(self) -> None:
fname = QtWidgets.QFileDialog.getOpenFileName(self, 'Select .pkl file', filter='pkl file (*.pkl)') fname = QtWidgets.QFileDialog.getOpenFileName(self, 'Select .pkl file', filter='pkl file (*.pkl)')
if fname[0] != '': if fname[0] != '':
self.filterView.load_and_update_from_dataset(fname[0]) try:
self.activeMode.update_measure_viewItems() 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__': if __name__ == '__main__':
......
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 CrossBoxSelector, SpiralSelector from geometricMethods import BoxSelectionSubsamplingMethod, CrossBoxSubSampling, SpiralBoxSubsampling
class MeasureMode(QtCore.QObject): class MeasureMode(QtCore.QObject):
def __init__(self, relatedFilterView: FilterView): def __init__(self, relatedFilterView: FilterView):
super(MeasureMode, self).__init__() super(MeasureMode, self).__init__()
self.filterView = relatedFilterView self.filterView: FilterView = relatedFilterView
self.uiControls: QtWidgets.QGroupBox = QtWidgets.QGroupBox() self.uiControls: QtWidgets.QGroupBox = QtWidgets.QGroupBox()
self.boxGenerator: BoxSelectionSubsamplingMethod = None
def get_control_groupBox(self) -> QtWidgets.QGroupBox: def get_control_groupBox(self) -> QtWidgets.QGroupBox:
return self.uiControls return self.uiControls
...@@ -15,29 +16,35 @@ class MeasureMode(QtCore.QObject): ...@@ -15,29 +16,35 @@ class MeasureMode(QtCore.QObject):
def update_measure_viewItems(self) -> None: def update_measure_viewItems(self) -> None:
raise NotImplementedError 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): 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.crossBoxGenerator: CrossBoxSelector = CrossBoxSelector(None) self.boxGenerator: CrossBoxSubSampling = CrossBoxSubSampling(None)
self.update_measure_viewItems() self.update_measure_viewItems()
def update_measure_viewItems(self) -> None: def update_measure_viewItems(self) -> None:
self.crossBoxGenerator.filterDiameter = self.filterView.filter.diameter self.boxGenerator.filterDiameter = self.filterView.filter.diameter
self.crossBoxGenerator.numBoxesAcross = int(self.uiControls.numBoxesSelector.currentText()) self.boxGenerator.numBoxesAcross = int(self.uiControls.numBoxesSelector.currentText())
desiredCoverage: int = self.uiControls.coverageSpinbox.value() 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) self.uiControls.set_to_max_possible_coverage(maxCoverage)
if desiredCoverage > maxCoverage: if desiredCoverage > maxCoverage:
desiredCoverage = maxCoverage desiredCoverage = maxCoverage
self.crossBoxGenerator.fraction = desiredCoverage / 100 self.boxGenerator.fraction = desiredCoverage / 100
topLefts: list = self.crossBoxGenerator.get_topLeft_of_boxes() topLefts: list = self.boxGenerator.get_topLeft_of_boxes()
boxSize = self.crossBoxGenerator.boxSize boxSize = self.boxGenerator.boxSize
self.filterView.update_measure_boxes(topLefts, boxSize) self.filterView.update_measure_boxes(topLefts, boxSize)
self._send_measuredParticles_to_filterview()
class CrossBoxesControls(QtWidgets.QGroupBox): class CrossBoxesControls(QtWidgets.QGroupBox):
...@@ -84,23 +91,19 @@ class SpiralBoxMode(MeasureMode): ...@@ -84,23 +91,19 @@ class SpiralBoxMode(MeasureMode):
def __init__(self, *args): def __init__(self, *args):
super(SpiralBoxMode, self).__init__(*args) super(SpiralBoxMode, self).__init__(*args)
self.uiControls: SpiralBoxControls = SpiralBoxControls(self) self.uiControls: SpiralBoxControls = SpiralBoxControls(self)
self.spiralBoxGenerator: SpiralSelector = SpiralSelector(None) self.boxGenerator: SpiralBoxSubsampling = SpiralBoxSubsampling(None)
self.update_measure_viewItems() self.update_measure_viewItems()
def update_measure_viewItems(self) -> None: def update_measure_viewItems(self) -> None:
self.spiralBoxGenerator.filterDiameter = self.filterView.filter.diameter self.boxGenerator.filterDiameter = self.filterView.filter.diameter
self.spiralBoxGenerator.boxSize = self.uiControls.boxSizeSpinbox.value()
# minBoxSize: float = self.spiralBoxGenerator.filterDiameter*0.1
# if self.spiralBoxGenerator.boxSize < minBoxSize:
# self.spiralBoxGenerator.boxSize = minBoxSize
# self.uiControls.boxSizeSpinbox.setValue(int(round(minBoxSize)))
self.spiralBoxGenerator.numBoxes = self.uiControls.numBoxesSpinbox.value() self.boxGenerator.numBoxes = self.uiControls.numBoxesSpinbox.value()
self.boxGenerator.fraction = self.uiControls.coverageSpinbox.value() / 100
topLefts: list = self.spiralBoxGenerator.get_topLeft_of_boxes() topLefts: list = self.boxGenerator.get_topLeft_of_boxes()
boxSize = self.spiralBoxGenerator.boxSize boxSize = self.boxGenerator.boxSize
self.filterView.update_measure_boxes(topLefts, boxSize) self.filterView.update_measure_boxes(topLefts, boxSize)
self._send_measuredParticles_to_filterview()
class SpiralBoxControls(QtWidgets.QGroupBox): class SpiralBoxControls(QtWidgets.QGroupBox):
...@@ -115,20 +118,23 @@ class SpiralBoxControls(QtWidgets.QGroupBox): ...@@ -115,20 +118,23 @@ class SpiralBoxControls(QtWidgets.QGroupBox):
layout = QtWidgets.QHBoxLayout() layout = QtWidgets.QHBoxLayout()
self.setLayout(layout) 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:')) layout.addWidget(QtWidgets.QLabel('Num Boxes:'))
self.numBoxesSpinbox = QtWidgets.QSpinBox() self.numBoxesSpinbox = QtWidgets.QSpinBox()
self.numBoxesSpinbox.setValue(10) self.numBoxesSpinbox.setValue(10)
self.numBoxesSpinbox.valueChanged.connect(self._config_changed) self.numBoxesSpinbox.valueChanged.connect(self._config_changed)
layout.addWidget(self.numBoxesSpinbox) 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): 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): ...@@ -32,39 +32,47 @@ class ParticleBinSorter(object):
return binIndex 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. Calculates, if a contour is overlapping a box.
:param boxTopLeftXY: topLeft of Box :param boxTopLeftXY: topLeft of Box
:param boxWidthHeight: Width and height of box :param boxWidthHeight: Width and height of box
:param contour: np.ndarrayof contour data :param contourData: np.ndarrayof contour data
:param offset: optional offset (x, y) of the box (i.e., the (0, 0) of the contours coord system does not match
the (0, 0) of the box coord system.
:return: :return:
""" """
contourPolygon = QtGui.QPolygonF() isOverlapping: bool = False
if type(contour) == np.ndarray:
for point in contour: xmin, xmax = np.min(contourData[:, 0, 0]), np.max(contourData[:, 0, 0])
contourPolygon.append(QtCore.QPointF(point[0, 0], point[0, 1])) width: float = xmax - xmin
elif type(contour) == QtGui.QPolygonF: boxXmin, boxXmax = boxTopLeftXY[0], boxTopLeftXY[0] + boxWidthHeight[0]
contourPolygon = contour
else: if xmin > (boxXmin-width/2):
raise TypeError if xmax < (boxXmax+width/2):
ymin, ymax = np.min(contourData[:, 0, 1]), np.max(contourData[:, 0, 1])
boxPolygon = QtGui.QPolygonF() height = ymax - ymin
boxPolygon.append(QtCore.QPointF(boxTopLeftXY[0]+offset[0], boxTopLeftXY[1]+offset[1])) boxYmin, boxYmax = boxTopLeftXY[1], boxTopLeftXY[1] + boxWidthHeight[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])) if ymin > (boxYmin-height/2):
boxPolygon.append(QtCore.QPointF(boxTopLeftXY[0]+offset[0] + boxWidthHeight[0], if ymax < (boxYmax+width/2):
boxTopLeftXY[1] + boxWidthHeight[1]+offset[1])) isOverlapping = True
isOverlapping: bool = boxPolygon.intersects(contourPolygon)
if not isOverlapping:
# sometimes, the polygon.intersects method does not capture everything... We test the brects therefore..
polygonBrect: QtCore.QRectF = contourPolygon.boundingRect()
boxBrect: QtCore.QRectF = boxPolygon.boundingRect()
if boxBrect.contains(polygonBrect) or boxBrect.intersects(polygonBrect):
isOverlapping = True
return isOverlapping 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): ...@@ -16,60 +16,90 @@ class SubsamplingMethod(object):
self.particleContainer = particleConatainer self.particleContainer = particleConatainer
self.fraction = desiredFraction 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 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. were measured by applying that subsampling procedure. Also, the actualy measured fraction is returned.
(The desired fraction may not always be achievable) (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 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): 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) numOrigParticles = len(self.particleContainer.particles)
numParticles = self._get_number_of_random_particles(numOrigParticles) numParticles = self._get_number_of_random_particles(numOrigParticles)
subParticles = random.sample(self.particleContainer.particles, numParticles) subParticles = random.sample(self.particleContainer.particles, numParticles)
return self.fraction, subParticles return subParticles
def _get_number_of_random_particles(self, numTotalParticles): def _get_number_of_random_particles(self, numTotalParticles):
return np.int(np.ceil(numTotalParticles * self.fraction)) return np.int(np.ceil(numTotalParticles * self.fraction))
def equals(self, otherMethod) -> bool:
class IvlevaSubsampling(SubsamplingMethod): return type(otherMethod) == type(self) and otherMethod.fraction == self.fraction
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)
class SizeBinFractioning(SubsamplingMethod): class SizeBinFractioning(SubsamplingMethod):
def __init__(self, particleConatiner, desiredfraction: float = 0.2): def __init__(self, particleConatiner, desiredfraction: float = 0.2):
super(SizeBinFractioning, self).__init__(particleConatiner, desiredfraction) super(SizeBinFractioning, self).__init__(particleConatiner, desiredfraction)
self.sorter: ParticleBinSorter = ParticleBinSorter() 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) subParticlesPerBin: list = self._get_subParticles_per_bin(self.particleContainer.particles)
subParticles: list = [] subParticles: list = []
for subParticleList in subParticlesPerBin: for subParticleList in subParticlesPerBin:
for particle in subParticleList: for particle in subParticleList:
subParticles.append(particle) subParticles.append(particle)
return self.fraction, subParticles return subParticles
def _get_subParticles_per_bin(self, particleList: list): def _get_subParticles_per_bin(self, particleList: list):
particlesInBins: list = self.sorter.sort_particles_into_bins(particleList) particlesInBins: list = self.sorter.sort_particles_into_bins(particleList)
...@@ -84,3 +114,6 @@ class SizeBinFractioning(SubsamplingMethod): ...@@ -84,3 +114,6 @@ class SizeBinFractioning(SubsamplingMethod):
subParticlesPerBin.append(subParticlesInBin) subParticlesPerBin.append(subParticlesInBin)
return subParticlesPerBin 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 matplotlib.pyplot as plt
import time import time
import sys
sys.path.append("C://Users//xbrjos//Desktop//Python")
from gepard import dataset
from methods import IvlevaSubsampling, RandomSampling, SizeBinFractioning from evaluation import TotalResults, SampleResult
from geometricMethods import BoxSelectionCreator from input_output import get_pkls_from_directory, get_attributes_from_foldername, save_results, load_results
from helpers import ParticleBinSorter
from evaluation import ResultComparer
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' IMPORTANT!!!
# fname: str = r'C:\Users\xbrjos\Desktop\temp MP\190326_MCII_WWTP_SB_50_1\190326_MCII_WWTP_SB_50_1.pkl' SET GEPARD TO EVALUATION BRANCH (WITHOUT THE TILING STUFF), OTHERWISE SOME OF THE LEGACY CONVERTS MIGHT FAIL..
# 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..
dset = dataset.loadData(fname) # results: TotalResults = TotalResults()
print('loaded dataset') # pklsInFolders = get_pkls_from_directory(r'C:\Users\xbrjos\Desktop\temp MP\NewDatasets')
#
boxCreator = BoxSelectionCreator(dset) # for folder in pklsInFolders.keys():
center, size = boxCreator.get_filterDimensions_from_dataset() # for samplePath in pklsInFolders[folder]:
print(center, size) # newSampleResult: SampleResult = results.add_sample(samplePath)
print(dset.mapToPixel(center, force=True)) # for attr in get_attributes_from_foldername(folder):
# newSampleResult.set_attribute(attr)
# 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()
# t0 = time.time() # t0 = time.time()
# fractions = np.arange(0.05, .55, 0.05) # results.update_all()
# errors = [] # print('updating all took', time.time()-t0, 'seconds')
# binErrors = [] #
# numIterations = 1000 # save_results('results1.res', results)
results: TotalResults = load_results('results1.res')
# for fraction in fractions:
# print('random sampling, fraction:', fraction) errorPerFraction: dict = results.get_error_vs_fraction_data(methods=['spiral', 'cross'])
# # randomSampling = RandomSampling(pc, desiredFraction=fraction) plt.clf()
# randomSampling = SizeBinFractioning(pc, fraction) for methodLabel in errorPerFraction.keys():
# iterErrors = [] fractions: list = list(errorPerFraction[methodLabel].keys())
# binIterErrors = [] errors: list = list(errorPerFraction[methodLabel].values())
# for _ in range(numIterations): plt.plot(fractions, errors, label=methodLabel)
# randomFraction, randomParticles = randomSampling.apply_subsampling_method()
# iterErrors.append(resultComparer._get_mp_count_error(origParticles, randomParticles, randomFraction)) plt.title('Spiral or Box Layouts')
# bins, errorsPerBin = resultComparer._get_mp_count_error_per_bin(origParticles, randomParticles, randomFraction) plt.xscale('log')
# binIterErrors.append(errorsPerBin) plt.xlabel('measured fraction')
plt.ylabel('mpCountError')
# errors.append(round(np.mean(iterErrors)*100)) #from fraction to % plt.legend()
# fractionBinErrors = [] plt.show()
# 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()
# # 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): ...@@ -68,6 +68,25 @@ class TestBinSorter(unittest.TestCase):
class TestOther(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): def test_box_overlaps_contour(self):
boxXY: tuple = 0, 0 boxXY: tuple = 0, 0
boxWidthHeight: tuple = 10, 10 boxWidthHeight: tuple = 10, 10
...@@ -78,15 +97,14 @@ class TestOther(unittest.TestCase): ...@@ -78,15 +97,14 @@ class TestOther(unittest.TestCase):
contourPoints = np.array([[[1, 1]], [[5, 5]], [[3, 3]]]) # fully enclosed contourPoints = np.array([[[1, 1]], [[5, 5]], [[3, 3]]]) # fully enclosed
self.assertTrue(helpers.box_overlaps_contour(boxXY, boxWidthHeight, contourPoints)) 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)) 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)) 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): def test_get_overlapping_fraction(self):
polygon1: QtGui.QPolygonF = QtGui.QPolygonF() 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 ...@@ -13,7 +13,8 @@ import numpy as np
import gepard import gepard
from gepard.analysis.particleContainer import ParticleContainer from gepard.analysis.particleContainer import ParticleContainer
from gepard.analysis.particleAndMeasurement import Particle 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 from helpers import ParticleBinSorter
...@@ -23,28 +24,6 @@ def get_default_particle_container(numParticles=1000): ...@@ -23,28 +24,6 @@ def get_default_particle_container(numParticles=1000):
return particleContainer 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): class TestRandomParticles(unittest.TestCase):
def test_get_number_of_random_particles(self): def test_get_number_of_random_particles(self):
randomSampling = RandomSampling(None, desiredFraction=0.1) randomSampling = RandomSampling(None, desiredFraction=0.1)
...@@ -87,5 +66,82 @@ class TestSizeBinFractioning(unittest.TestCase): ...@@ -87,5 +66,82 @@ class TestSizeBinFractioning(unittest.TestCase):
self.assertEqual(len(subParticles), numParticlesPerBinExpected) self.assertEqual(len(subParticles), numParticlesPerBinExpected)
if __name__ == '__main__': class TestMethodEquality(unittest.TestCase):
unittest.main() 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))