Commit fa14c5fa authored by Josef Brandt's avatar Josef Brandt

applying subsampling now is also used for updating the gui

Also, it now only returns the list of measured particles..
parent 95a7feb0
......@@ -5,11 +5,30 @@ Created on Wed Jan 22 13:57:28 2020
@author: luna
"""
import pickle
import os
from helpers import ParticleBinSorter
class ResultComparer(object):
class ResultObject(object):
"""
An object the actually stores all generated results per sample and can update and report on them.
"""
def __init__(self, filepath: str):
super(ResultObject, self).__init__()
self.filepath: str = filepath
print(self.sampleName)
# noinspection PyTypeChecker
@property
def sampleName(self) -> str:
return os.path.basename(self.filepath).split('.')[0]
class ResultComparer(object):
def _get_mp_count_error_per_bin(self, allParticles, subParticles, fractionMeasured):
binSorter = ParticleBinSorter()
allParticlesInBins = binSorter.sort_particles_into_bins(allParticles)
......@@ -47,4 +66,4 @@ class ResultComparer(object):
numMPParticles += 1
break
return numMPParticles
\ No newline at end of file
return numMPParticles
......@@ -22,20 +22,38 @@ class BoxSelectionSubsamplingMethod(SubsamplingMethod):
def filterArea(self) -> float:
return np.pi * (self.filterDiameter / 2) ** 2
def apply_subsampling_method(self) -> tuple:
def apply_subsampling_method(self) -> list:
def distanceToCnt(topleft: tuple):
return abs(topleft[0] - cntStart[0]) + abs(topleft[1] - cntStart[1])
subParticles: list = []
topLefts: list = self.get_topLeft_of_boxes()
boxSize = self.boxSize
boxWidthHeight: tuple = (self.boxSize, self.boxSize)
for particle in self.particleContainer.particles:
for topLeft in topLefts:
if box_overlaps_contour(topLeft, (boxSize, boxSize), particle.contour, self.offset):
cntStart: tuple = (particle.contour[0, 0, 0], particle.contour[0, 0, 1])
sortedTopLefts = sorted(topLefts, key=distanceToCnt)
for topLeftXY in sortedTopLefts:
if helpers.box_overlaps_contour(topLeftXY, boxWidthHeight, particle.contour):
subParticles.append(particle)
break
return self.fraction, subParticles
return subParticles
def get_topLeft_of_boxes(self) -> list:
raise NotImplementedError
def _apply_offset_to_toplefts(self, topLefts: list) -> list:
"""
Applies the filter offset to the calculated topLefts of the measure boxes.
:param topLefts:
:return:
"""
newTopLefts: list = []
for topLeft in topLefts:
newTopLefts.append((topLeft[0] + self.offset[0], topLeft[1] + self.offset[1]))
return newTopLefts
class BoxSelectionCreator(object):
def __init__(self, dataset: dataset.DataSet):
......@@ -122,7 +140,7 @@ class CrossBoxSelector(BoxSelectionSubsamplingMethod):
if i != self.numBoxesAcross // 2:
topLeftCorners.append((xStartCoordinates[i], middleYCoordinate))
return topLeftCorners
return self._apply_offset_to_toplefts(topLeftCorners)
def get_maximum_achievable_fraction(self) -> float:
"""
......@@ -216,7 +234,7 @@ class SpiralSelector(BoxSelectionSubsamplingMethod):
boxDistance *= 1.05
topLefts = self._move_and_scale_toplefts(topLefts)
return topLefts
return self._apply_offset_to_toplefts(topLefts)
def _move_and_scale_toplefts(self, topLefts: list) -> list:
"""
......
......@@ -32,36 +32,20 @@ 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:
try:
self.dataset = dataset.loadData(fname)
except IndexError:
self.dataset = None
QtWidgets.QMessageBox.critical(self, 'Index Error', 'Unable to load dataset..')
if self.dataset is not None:
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._fit_to_window()
def update_from_dataset(self, dset: dataset.DataSet) -> None:
self.dataset = dset
self._update_particle_contours()
self._fit_to_window()
def _update_particle_contours(self) -> None:
self._remove_particle_contours()
......@@ -76,22 +60,10 @@ class FilterView(QtWidgets.QGraphicsView):
self.scene().removeItem(cntItem)
self.contourItems = []
def _update_measured_contours(self) -> None:
def distanceToCnt(box: MeasureBoxGraphItem):
return abs(box.posX - cntStart[0]) + abs(box.posY - cntStart[1])
for contourItem in self.contourItems:
contourItem.isMeasured = False
cntStart: tuple = (contourItem.contourData[0, 0, 0], contourItem.contourData[0, 0, 1])
sortedmeasBoxes = sorted(self.measuringBoxes, key=distanceToCnt)
for measBox in sortedmeasBoxes:
topLeftXY = (measBox.posX, measBox.posY)
boxWidthHeight = (measBox.width, measBox.height)
if helpers.box_overlaps_contour(topLeftXY, boxWidthHeight, contourItem.contourData):
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, CrossBoxSelector, SpiralSelector
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: CrossBoxSelector = CrossBoxSelector(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,18 +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: SpiralSelector = SpiralSelector(None)
self.update_measure_viewItems()
def update_measure_viewItems(self) -> None:
self.spiralBoxGenerator.filterDiameter = self.filterView.filter.diameter
self.boxGenerator.filterDiameter = self.filterView.filter.diameter
self.spiralBoxGenerator.numBoxes = self.uiControls.numBoxesSpinbox.value()
self.spiralBoxGenerator.fraction = self.uiControls.coverageSpinbox.value() / 100
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):
......
......@@ -24,12 +24,12 @@ class SubsamplingMethod(object):
"""
raise NotImplementedError
def apply_subsampling_method(self) -> tuple:
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
......@@ -39,11 +39,11 @@ class RandomSampling(SubsamplingMethod):
def label(self) -> str:
return 'Random Subsampling'
def apply_subsampling_method(self):
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))
......@@ -60,12 +60,12 @@ class IvlevaSubsampling(SubsamplingMethod):
def label(self) -> str:
return 'Random fraction, Anger et al.'
def apply_subsampling_method(self):
def apply_subsampling_method(self) -> list:
N = self.particleContainer.getNumberOfParticles()
numParticlesMeasured = self._get_ivleva_fraction(N)
subParticles = random.sample(self.particleContainer.particles, numParticlesMeasured)
fractionMeasured = numParticlesMeasured/N
return fractionMeasured, subParticles
return subParticles
def _get_ivleva_fraction(self, N):
P = self.estimatedMPFraction
......@@ -83,13 +83,13 @@ class SizeBinFractioning(SubsamplingMethod):
def label(self) -> str:
return 'SizeBin Random Subsampling'
def apply_subsampling_method(self):
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)
......
......@@ -9,7 +9,7 @@ import gepardevaluation
from methods import IvlevaSubsampling, RandomSampling, SizeBinFractioning
from geometricMethods import BoxSelectionCreator
from helpers import ParticleBinSorter
from evaluation import ResultComparer
from evaluation import ResultComparer, ResultObject
"""
......@@ -28,8 +28,9 @@ workingFiles.append(r'C:\Users\xbrjos\Desktop\temp MP\190201_BSB_Stroomi_ds2_R1_
# fname: str = r'C:\Users\xbrjos\Desktop\temp MP\KWS_CT_3_ds1_all_10_2\KWS_CT_3_ds1_all_10_2.pkl'
for index, fname in enumerate(workingFiles):
dset = dataset.loadData(fname)
print('loaded dataset', fname)
# dset = dataset.loadData(fname)
newObj: ResultObject = ResultObject(fname)
# print('loaded dataset', fname)
# boxCreator = BoxSelectionCreator(dset)
# center, size = boxCreator.get_filterDimensions_from_dataset()
......
import sys
import unittest
import numpy as np
from geometricMethods import CrossBoxSelector, SpiralSelector, BoxSelectionCreator
from geometricMethods import BoxSelectionSubsamplingMethod, CrossBoxSelector, SpiralSelector, BoxSelectionCreator
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)
def test_apply_offset_to_toplefts(self):
topLefts = [(0, 0), (10, 10), (-5, -5), (0, 20)]
self.boxSelector.offset = (0, 0)
newTopLefts: list = self.boxSelector._apply_offset_to_toplefts(topLefts)
self.assertEqual(newTopLefts[0], (0, 0))
self.assertEqual(newTopLefts[1], (10, 10))
self.assertEqual(newTopLefts[2], (-5, -5))
self.assertEqual(newTopLefts[3], (0, 20))
self.boxSelector.offset = (10, 0)
newTopLefts: list = self.boxSelector._apply_offset_to_toplefts(topLefts)
self.assertEqual(newTopLefts[0], (10, 0))
self.assertEqual(newTopLefts[1], (20, 10))
self.assertEqual(newTopLefts[2], (5, -5))
self.assertEqual(newTopLefts[3], (10, 20))
self.boxSelector.offset = (10, -20)
newTopLefts: list = self.boxSelector._apply_offset_to_toplefts(topLefts)
self.assertEqual(newTopLefts[0], (10, -20))
self.assertEqual(newTopLefts[1], (20, -10))
self.assertEqual(newTopLefts[2], (5, -25))
self.assertEqual(newTopLefts[3], (10, 0))
class TestSelectCrossBoxes(unittest.TestCase):
def setUp(self) -> None:
self.crossBoxSelector = CrossBoxSelector(None)
......
......@@ -41,7 +41,7 @@ class TestIvleva(unittest.TestCase):
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()
ivlevaParticles = ivlevaSampling.apply_subsampling_method()
self.assertEqual(len(ivlevaParticles), numParticles)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment