Commit b02245b5 authored by Josef Brandt's avatar Josef Brandt

Merge branch 'ResultGeneration'

parents f91aed7d a8488af0
......@@ -6,3 +6,11 @@ __pycache__/
*.png
*.res
cythonModules/build/
*.c
*.pyd
*.html
import numpy as np
import cv2
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.cluster import DBSCAN
from scipy import spatial
from itertools import combinations
from random import sample
import sys
sys.path.append("C://Users//xbrjos//Desktop//Python")
from gepard.analysis.particleContainer import ParticleContainer
from gepard.analysis import particleAndMeasurement as pm
from methods import SubsamplingMethod
def get_pca(data: np.ndarray, numComp: int = 2) -> np.ndarray:
try:
standardizedData = StandardScaler().fit_transform(data.copy())
except ValueError:
print('first standardscaler attempt failed, retrying..')
print('datashape', data.shape)
print('unique:', np.unique(data))
raise
pca = PCA(n_components=numComp)
princComp: np.ndarray = pca.fit_transform(np.transpose(standardizedData))
return princComp
def do_DBSCAN_clustering(data: np.ndarray, eps: float = 0.1, min_samples: int = 10) -> tuple:
"""
Does DBSCAN clustering and finds noisy data
:param data: The input array
:param eps:
:param min_samples:
:return: Cluster labels for each point in the dataset given to fit(). Noisy samples are given the label -1.
"""
assert data.shape[1] == 2
standardizedData = StandardScaler().fit_transform(data)
db = DBSCAN(eps=eps, min_samples=min_samples).fit(standardizedData)
return db.labels_, db.core_sample_indices_
def get_n_points_closest_to_point(points: np.ndarray, n: int, refPoint: np.ndarray) -> list:
"""
Returns a list with indices of n points that are closest to the indicated refPoint
:param points: np.ndarray, cols: x, y, rows: individual points
:param n: number of points to return
:param refPoint: np.array([x, y]) of reference point
:return: list of point indices
"""
distancesToPoints: np.ndarray = np.linalg.norm(points - refPoint, axis=1)
sortedIndices = np.argsort(distancesToPoints)
return list(sortedIndices[:n])
class ChemometricSubsampling(SubsamplingMethod):
def __init__(self, particleContainer: ParticleContainer, desiredFraction: float):
super(ChemometricSubsampling, self).__init__(particleContainer, desiredFraction)
@property
def label(self) -> str:
return 'Chemometric Selection'
def apply_subsampling_method(self) -> list:
vectors: np.ndarray = self._get_particle_featurematrix()
try:
princComps: np.ndarray = get_pca(vectors)
except ValueError:
print('numParticles:', len(self.particleContainer.particles))
print('input featurematrix shape', vectors.shape)
clusterLabels, coreIndices = do_DBSCAN_clustering(princComps)
indices: list = self._get_indices_from_clusterLabels(princComps, clusterLabels, coreIndices)
selectedParticles: list = []
for particle in self.particleContainer.particles:
if particle.index in indices:
selectedParticles.append(particle)
return selectedParticles
def _get_particle_featurematrix(self) -> np.ndarray:
"""
:return: np.ndarray, numRows: Features, numCols: Particles
"""
vectors: list = []
for particle in self.particleContainer.particles:
extractor: FeatureExtractor = FeatureExtractor(particle)
vectors.append(extractor.get_characteristic_vector())
vectors: np.ndarray = np.transpose(np.array(vectors))
assert vectors.shape == (11, len(self.particleContainer.particles)), f'wrong featureMat-shape: {vectors.shape}'
return vectors
def equals(self, otherMethod) -> bool:
equals: bool = False
if type(otherMethod) == ChemometricSubsampling and otherMethod.fraction == self.fraction:
equals = True
return equals
def _get_indices_from_clusterLabels(self, points: np.ndarray, labels: np.ndarray, centerIndices: np.ndarray) -> list:
indices: list = []
allIndices: np.ndarray = np.arange(len(labels))
numPointsPerCluster: dict = self._get_numPoints_per_cluster(labels)
for clusterIndex in set(labels):
indToAppend: list = []
nPoints: int = int(numPointsPerCluster[clusterIndex])
indicesInCluster: np.ndarray = allIndices[labels == clusterIndex]
if clusterIndex == -1:
for ind in sample(list(indicesInCluster), nPoints):
# assert ind not in indices
indices.append(ind)
else:
clusterPoints: np.ndarray = points[indicesInCluster]
centerPoint: np.ndarray = np.mean(clusterPoints, axis=0)
indicesToSelect: list = get_n_points_closest_to_point(clusterPoints, nPoints, centerPoint)
for ind in indicesToSelect:
origInd = indicesInCluster[ind]
indices.append(origInd)
assert len(set(indices)) == len(indices), f'The calculated indices contain duplicates, ' \
f'num duplicates: {len(indices) - len(set(indices))}'
return indices
def _get_numPoints_per_cluster(self, labels: np.ndarray, noiseAmpFactor: float = 5) -> dict:
"""
MP Particles are expected to be the minority of all particles. So, if datapoints were classified as noise
(i.e., label = -1), it is likely that MP is in there. The abundancy of points taken from the noise is multiplied
by the noiseAmpFactor
:param labels:
:param noiseAmpFactor:
:return: A dictionary with keys = cluster index (i.e., label) and value = number of points to take from that
"""
pointsPerCluster: dict = {}
if type(labels) != np.ndarray:
labels = np.array(labels)
individualLabels: set = set(labels)
numPointsToSelect = round(len(labels) * self.fraction)
if numPointsToSelect == 0:
numPointsToSelect = 1
numNoisePoints = len(labels[labels == -1])
numClusteredPoints = len(labels) - numNoisePoints
# # get max noiseAmpFactor
if noiseAmpFactor > 1/self.fraction:
noiseAmpFactor = 1/self.fraction
numAmpPoints = numClusteredPoints + numNoisePoints*noiseAmpFactor
fractionPerCluster = np.clip(numPointsToSelect / numAmpPoints, 0.0, 1.0)
tooFewPoints = numPointsToSelect < len(individualLabels)
totalPointsAdded = 0
for ind in individualLabels:
if ind > -1:
if not tooFewPoints:
pointsToAdd = round(fractionPerCluster * len(labels[labels == ind]))
else:
pointsToAdd = 1 if totalPointsAdded < numPointsToSelect else 0
pointsPerCluster[ind] = pointsToAdd
totalPointsAdded += pointsToAdd
# fill up the rest with noisePoints
if numNoisePoints > 0:
diff: float = np.clip(numPointsToSelect - totalPointsAdded, 0, numNoisePoints)
pointsPerCluster[-1] = diff
totalPointsAdded += diff
# just in case too many points were selected (due to rounding errors), keep on deleting until it matches
while totalPointsAdded > numPointsToSelect:
indexWithHighestCount = None
maxCount = 0
for index in pointsPerCluster.values():
if pointsPerCluster[index] > maxCount:
maxCount = pointsPerCluster[index]
indexWithHighestCount = index
pointsPerCluster[indexWithHighestCount] -= 1
totalPointsAdded -= 1
if not abs(totalPointsAdded - numPointsToSelect) <= 1:
print('error')
# assert abs(totalPointsAdded - numPointsToSelect) <= 1
for clusterIndex in pointsPerCluster.keys():
assert 0 <= pointsPerCluster[clusterIndex] <= len(labels[labels == clusterIndex])
return pointsPerCluster
class FeatureExtractor(object):
def __init__(self, particle: pm.Particle):
super(FeatureExtractor, self).__init__()
self.particle: pm.Particle = particle
def get_characteristic_vector(self) -> np.ndarray:
log_hu: np.ndarray = self._get_log_hu_moments()
color: np.ndarray = self._get_color_hash(self.particle.color, desiredLength=4)
vector: np.ndarray = np.hstack((log_hu, color))
if len(vector) != 11:
print('error')
assert len(vector) == 7 + 4, f'wrong feature vector: {vector} with shape: {vector.shape}'
return vector
def _get_log_hu_moments(self) -> np.ndarray:
moments: dict = cv2.moments(self.particle.contour)
resultMoments: np.ndarray = np.zeros((7, 1))
for index, mom in enumerate(cv2.HuMoments(moments)):
if mom != 0:
resultMoments[index] = -1 * np.copysign(1.0, mom) * np.log10(abs(mom))
else:
resultMoments[index] = 0
return resultMoments[:, 0]
def _get_color_hash(self, color: str, desiredLength: int = 4) -> np.ndarray:
colorArray: list = [int(i) for i in str(abs(hash(color)))[:desiredLength]]
return np.transpose(np.array(colorArray))
import numpy as np
cimport numpy as np
cimport cython
DTYPE = np.float
ctypedef np.int32_t INT32_t
def rotate_contour_around_point(np.ndarray[INT32_t, ndim=3] contour, np.ndarray[INT32_t, ndim=1] refPoint, np.float angleDegree):
"""
Rotates a point around another one... All coordinates in pixel space (integers)
:param contour: Array of points to be rotated, [:, 0, 0] = x, [:, 0, 1] = y
:param refPoint: The referemce point around which the first point is rotated, tuple of x and y
:param angleDegree: The angle in degree to rotate (counter-clockwise)
:return: Array of the rotated point, [:, 0, 0] = x, [:, 0, 1] = y
"""
cdef int i
cdef double theta, sin, cos, x, y
cdef np.ndarray[INT32_t, ndim=3] newContour
theta = np.deg2rad(angleDegree)
sin = np.sin(theta)
cos = np.cos(theta)
newContour = np.zeros_like(contour)
for i in range(contour.shape[0]):
x = cos * (contour[i, 0, 0]-refPoint[0]) - sin * (contour[i, 0, 1]-refPoint[1]) + refPoint[0]
y = sin * (contour[i, 0, 0]-refPoint[0]) + cos * (contour[i, 0, 1]-refPoint[1]) + refPoint[1]
newContour[i, 0, 0] = round(x)
newContour[i, 0, 1] = round(y)
return newContour
# try:
from setuptools import setup
from setuptools import Extension
from Cython.Build import cythonize
import numpy as np
import sys
if len(sys.argv) == 1:
sys.argv.append("build_ext")
sys.argv.append("--inplace")
ext = Extension("rotateContour", ["rotateContour.pyx"], extra_compile_args=['-O3'],)
setup(
name="rotate contour around reference point",
ext_modules=cythonize([ext], annotate=True), # accepts a glob pattern
include_dirs=[np.get_include()]
)
\ No newline at end of file
import copy
import numpy as np
import sys
sys.path.append("C://Users//xbrjos//Desktop//Python")
from gepard.analysis.particleContainer import ParticleContainer
from cythonModules import rotateContour
class ParticleVariations(object):
def __init__(self, particleContainer: ParticleContainer, numVariations: int = 10) -> None:
super(ParticleVariations, self).__init__()
self.origParticleContainer = particleContainer
self.numVariations = numVariations
def get_particleContainer_variations(self) -> ParticleContainer:
if self.numVariations > 0:
partContainer: ParticleContainer = self.origParticleContainer
contours: list = partContainer.getParticleContours()
center: tuple = round(np.mean(contours[:][0][0])),\
round(np.mean(contours[:][0][1]))
center: np.ndarray = np.array(center, dtype=np.int32)
angles = self._get_angles()
for i in range(self.numVariations):
if i > 0:
partContainer = copy.deepcopy(self.origParticleContainer)
for particle in partContainer.particles:
contour = np.int32(particle.contour)
particle.contour = rotateContour.rotate_contour_around_point(contour,
center, np.float(angles[i]))
yield partContainer
def _get_angles(self) -> np.ndarray:
angleIncrement: float = 360 / self.numVariations
return np.arange(self.numVariations) * angleIncrement
This diff is collapsed.
from PyQt5 import QtGui, QtWidgets, QtCore
import numpy as np
import sys
sys.path.append("C://Users//xbrjos//Desktop//Python")
import gepard
from gepard import dataset
import helpers
import numpy as np
from cythonModules import rotateContour as rc
class FilterView(QtWidgets.QGraphicsView):
......@@ -14,6 +15,7 @@ class FilterView(QtWidgets.QGraphicsView):
self.setWindowTitle('FilterView')
self.dataset: dataset.DataSet = None
self.rotation: int = 0
scene = QtWidgets.QGraphicsScene(self)
scene.setItemIndexMethod(QtWidgets.QGraphicsScene.NoIndex)
......@@ -47,6 +49,20 @@ class FilterView(QtWidgets.QGraphicsView):
self._update_particle_contours()
self._fit_to_window()
@helpers.timingDecorator
def update_rotation(self, newRotation: int) -> None:
if newRotation != self.rotation:
angle: float = np.float(newRotation-self.rotation)
center: np.ndarray = np.array([self.filter.circleOffset[0] + self.filter.diameter/2,
self.filter.circleOffset[1] + self.filter.diameter/2], dtype=np.int32)
for particle in self.dataset.particleContainer.particles:
particle.contour = rc.rotate_contour_around_point(particle.contour, center, angle)
self._update_particle_contours()
self.rotation = newRotation
@helpers.timingDecorator
def _update_particle_contours(self) -> None:
self._remove_particle_contours()
if self.dataset is not None:
......
......@@ -25,14 +25,24 @@ class MainView(QtWidgets.QWidget):
loadDsetBtn = QtWidgets.QPushButton('Load Dataset')
loadDsetBtn.released.connect(self._load_dataset)
self.rotationSpinBox = QtWidgets.QSpinBox()
self.rotationSpinBox.setMinimum(0)
self.rotationSpinBox.setMaximum(359)
self.rotationSpinBox.setValue(0)
self.rotationSpinBox.setMaximumWidth(50)
self.rotationSpinBox.valueChanged.connect(self._update_fiter_rotation)
self.controlGroup = QtWidgets.QGroupBox()
self.controlGroupLayout = QtWidgets.QHBoxLayout()
self.controlGroup.setLayout(self.controlGroupLayout)
self.controlGroupLayout.addWidget(loadDsetBtn)
self.controlGroupLayout.addWidget(QtWidgets.QLabel('Filter Rotation'))
self.controlGroupLayout.addWidget(self.rotationSpinBox)
self.controlGroupLayout.addWidget(QtWidgets.QLabel('Select Subsampling Mode:'))
self.controlGroupLayout.addWidget(self.modeSelector)
self.controlGroupLayout.addWidget(self.activeModeControl)
self.controlGroupLayout.addStretch()
self.layout.addWidget(self.controlGroup)
self.filterView = FilterView()
......@@ -62,10 +72,11 @@ class MainView(QtWidgets.QWidget):
self.activeMode = requestedMode
self.activeModeControl = self.activeMode.get_control_groupBox()
self.controlGroupLayout.insertWidget(2, self.activeModeControl)
self.controlGroupLayout.insertWidget(3, self.activeModeControl)
self.activeMode.update_measure_viewItems()
@helpers.timingDecorator
def _load_dataset(self) -> None:
fname = QtWidgets.QFileDialog.getOpenFileName(self, 'Select .pkl file', filter='pkl file (*.pkl)')
if fname[0] != '':
......@@ -91,6 +102,10 @@ class MainView(QtWidgets.QWidget):
self.filterView.update_from_dataset(dset)
self.activeMode.update_measure_viewItems()
def _update_fiter_rotation(self):
self.filterView.update_rotation(self.rotationSpinBox.value())
self.activeMode.send_measuredParticles_to_filterview()
if __name__ == '__main__':
import sys
......
......@@ -9,6 +9,7 @@ class MeasureMode(QtCore.QObject):
self.filterView: FilterView = relatedFilterView
self.uiControls: QtWidgets.QGroupBox = QtWidgets.QGroupBox()
self.boxGenerator: BoxSelectionSubsamplingMethod = None
self.subParticles: list = []
def get_control_groupBox(self) -> QtWidgets.QGroupBox:
return self.uiControls
......@@ -16,7 +17,7 @@ class MeasureMode(QtCore.QObject):
def update_measure_viewItems(self) -> None:
raise NotImplementedError
def _send_measuredParticles_to_filterview(self) -> None:
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)
......@@ -44,7 +45,7 @@ class CrossBoxMode(MeasureMode):
topLefts: list = self.boxGenerator.get_topLeft_of_boxes()
boxSize = self.boxGenerator.boxSize
self.filterView.update_measure_boxes(topLefts, boxSize)
self._send_measuredParticles_to_filterview()
self.send_measuredParticles_to_filterview()
class CrossBoxesControls(QtWidgets.QGroupBox):
......@@ -103,7 +104,7 @@ class SpiralBoxMode(MeasureMode):
topLefts: list = self.boxGenerator.get_topLeft_of_boxes()
boxSize = self.boxGenerator.boxSize
self.filterView.update_measure_boxes(topLefts, boxSize)
self._send_measuredParticles_to_filterview()
self.send_measuredParticles_to_filterview()
class SpiralBoxControls(QtWidgets.QGroupBox):
......
......@@ -3,24 +3,42 @@ import numpy as np
import sys
sys.path.append("C://Users//xbrjos//Desktop//Python")
from gepard import dataset
import time
def timingDecorator(callingFunction):
"""
A wrapper function for timing the duration of the given function.
:param callingFunction:
:return: wrapped function
"""
def wrapper(*args, **kwargs):
t0 = time.time()
ret = callingFunction(*args, **kwargs)
print(f'{callingFunction.__name__} took {np.round(time.time()-t0, 2)} seconds')
return ret
return wrapper
class ParticleBinSorter(object):
def __init__(self):
super(ParticleBinSorter, self).__init__()
self.bins = [5, 10, 20, 50, 100, 200, 500]
def sort_particles_into_bins(self, particleList):
particlesInBins = self._get_empty_bins()
for particle in particleList:
binIndex = self._get_binIndex_of_particle(particle)
particlesInBins[binIndex].append(particle)
return particlesInBins
def _get_empty_bins(self):
return [[] for _ in range(len(self.bins)+1)]
def _get_binIndex_of_particle(self, particle):
size = particle.getParticleSize()
binIndex = 0
......@@ -114,7 +132,7 @@ def get_polygon_area(polygon: QtGui.QPolygonF) -> float:
return area
def get_filterDimensions_from_dataset(dataset) -> tuple:
def get_filterDimensions_from_dataset(dataset: dataset.DataSet) -> tuple:
"""
Processes the datasets boundary items to calculate diameter and offset (coord system offset of circular filter
with respect to actual dataset). This is used to set circular filter dimensions to use in the geometric
......
......@@ -14,7 +14,7 @@ class SubsamplingMethod(object):
def __init__(self, particleConatainer, desiredFraction: float = 0.2):
super(SubsamplingMethod, self).__init__()
self.particleContainer = particleConatainer
self.fraction = desiredFraction
self.fraction: float = desiredFraction
@property
def label(self) -> str:
......
......@@ -25,13 +25,13 @@ SET GEPARD TO EVALUATION BRANCH (WITHOUT THE TILING STUFF), OTHERWISE SOME OF TH
#
# save_results('results1.res', results)
results: TotalResults = load_results('results1.res')
results.update_all(force=True)
# results.update_all(force=True)
# save_results('results1.res', results)
plt.clf()
errorPerFraction: dict = results.get_error_vs_fraction_data(attributes=['air', 'water'],
methods=[])
# methods=['spiral', 'cross'])
plt.subplot(121)
for methodLabel in errorPerFraction.keys():
fractions: list = list(errorPerFraction[methodLabel].keys())
......@@ -39,11 +39,11 @@ for methodLabel in errorPerFraction.keys():
plt.plot(fractions, errors)
plt.scatter(fractions, errors, label=methodLabel)
plt.title('Spiral or Box Layouts on Air/Water sample', fontSize=15)
plt.title('Air/Water sample', fontSize=15)
plt.xscale('log')
plt.xlabel('measured fraction', fontsize=12)
plt.ylabel('mpCountError (%)', fontsize=12)
# plt.ylim([0, 1])
plt.ylim([0, 100])
plt.legend()
errorPerFraction: dict = results.get_error_vs_fraction_data(attributes=['sediment', 'soil', 'beach', 'slush'],
......@@ -55,11 +55,11 @@ for methodLabel in errorPerFraction.keys():
plt.plot(fractions, errors)
plt.scatter(fractions, errors, label=methodLabel)
plt.title('Spiral or Box Layouts on Sedimant/Beach/Slush sample', fontSize=15)
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, 1])
plt.ylim([0, 100])
plt.legend()
plt.show()
......
import numpy as np
import sys
sys.path.append("C://Users//xbrjos//Desktop//Python")
import gepard
from gepard.analysis.particleContainer import ParticleContainer
def get_default_ParticleContainer() -> ParticleContainer:
particleContainer: ParticleContainer = ParticleContainer(None)
particleContainer.initializeParticles(4)
contours: list = []
for i in range(4):
x = 10*i
contours.append(np.array([[[x, 0]], [[x+10, 0]], [[x+10, 10]], [[x, 10]]], dtype=np.int32))
particleContainer.setParticleContours(contours)
return particleContainer
This diff is collapsed.
import unittest
import numpy as np
from cythonModules import rotateContour as rc
class CythonTester(unittest.TestCase):
def test_rotate_contour(self):
contour: np.ndarray = np.array([[[0, 5]],
[[5, 5]],
[[5, 0]]], dtype=np.int32)
refPoint: np.ndarray = np.array([0, 0], dtype=np.int32)
angle: float = 90.0
newContour: np.ndarray = rc.rotate_contour_around_point(contour, refPoint, angle)
self.assertAlmostEqual(newContour[0, 0, 0], -5)
self.assertAlmostEqual(newContour[0, 0, 1], 0)
self.assertAlmostEqual(newContour[1, 0, 0], -5)
self.assertAlmostEqual(newContour[1, 0, 1], 5)
self.assertAlmostEqual(newContour[2, 0, 0], 0)
self.assertAlmostEqual(newContour[2, 0, 1], 5)
angle = 180.0
newContour = rc.rotate_contour_around_point(contour, refPoint, angle)
self.assertAlmostEqual(newContour[0, 0, 0], 0)
self.assertAlmostEqual(newContour[0, 0, 1], -5)
self.assertAlmostEqual(newContour[1, 0, 0], -5)
self.assertAlmostEqual(newContour[1, 0, 1], -5)
self.assertAlmostEqual(newContour[2, 0, 0], -5)
self.assertAlmostEqual(newContour[2, 0, 1], 0)
angle = 270.0
newContour = rc.rotate_contour_around_point(contour, refPoint, angle)
self.assertAlmostEqual(newContour[0, 0, 0], 5)
self.assertAlmostEqual(newContour[0, 0, 1], 0)
self.assertAlmostEqual(newContour[1, 0, 0], 5)
self.assertAlmostEqual(newContour[1, 0, 1], -5)
self.assertAlmostEqual(newContour[2, 0, 0], 0)
self.assertAlmostEqual(newContour[2, 0, 1], -5)
refPoint = np.array([5, 5], dtype=np.int32)
angle: float = 90.0
newContour = rc.rotate_contour_around_point(contour, refPoint, angle)
self.assertAlmostEqual(newContour[0, 0, 0], 5)
self.assertAlmostEqual(newContour[0, 0, 1], 0)
self.assertAlmostEqual(newContour[2, 0, 0], 10)
self.assertAlmostEqual(newContour[2, 0, 1], 5)
angle = 180.0
newContour = rc.rotate_contour_around_point(contour, refPoint, angle)
self.assertAlmostEqual(newContour[0, 0, 0], 10)
self.assertAlmostEqual(newContour[0, 0, 1], 5)
self.assertAlmostEqual(newContour[2, 0, 0], 5)
self.assertAlmostEqual(newContour[2, 0, 1], 10)
angle = 270.0
newContour = rc.rotate_contour_around_point(contour, refPoint, angle)
self.assertAlmostEqual(newContour[0, 0, 0], 5)
self.assertAlmostEqual(newContour[0, 0, 1], 10)
self.assertAlmostEqual(newContour[2, 0, 0], 0)
self.assertAlmostEqual(newContour[2, 0, 1], 5)
import unittest
import numpy as np
import sys
sys.path.append("C://Users//xbrjos//Desktop//Python")
from gepard.analysis.particleContainer import ParticleContainer
from datasetOperations import ParticleVariations
from helpers_for_test import get_default_ParticleContainer
class TestParticleVariations(unittest.TestCase):
def test_get_particleContainer_variations(self):
particleContainer: ParticleContainer = get_default_ParticleContainer()
contours = particleContainer.getParticleContours()
center: tuple = round(np.mean(contours[:][0][0])), \
round(np.mean(contours[:][0][1]))
center: np.ndarray = np.array(center, dtype=np.int32)
for numVariations in [0, 1, 10, 20]:
particleVariations: ParticleVariations = ParticleVariations(particleContainer, numVariations)
foundContours: list = []
if numVariations == 0:
self.assertEqual(len(list(particleVariations.get_particleContainer_variations())), 0)
else:
for index, partContainer in enumerate(particleVariations.get_particleContainer_variations()):
if index == 0:
self.assertTrue(partContainer is particleContainer)
else:
self.assertFalse(partContainer is particleContainer)
contours = []
for particle in partContainer.particles:
contours.append(particle.contour)
contourHash = hash(particle.contour.tostring())
self.assertTrue(contourHash not in foundContours)
foundContours.append(contourHash)
self.assertEqual(index, numVariations-1)
def test_get_angles(self):
particleVariations: ParticleVariations = ParticleVariations(None, 2)
angles: list = list(particleVariations._get_angles())
self.assertEqual(angles, [0, 180])
particleVariations.numVariations = 4
angles: list = list(particleVariations._get_angles())
self.assertEqual(angles, [0, 90, 180, 270])
This diff is collapsed.
......@@ -13,8 +13,9 @@ import numpy as np
import gepard
from gepard.analysis.particleContainer import ParticleContainer
from gepard.analysis.particleAndMeasurement import Particle
from methods import RandomSampling, SizeBinFractioning
from methods import SubsamplingMethod, RandomSampling, SizeBinFractioning
import geometricMethods as gmeth
import chemometricMethods as cmeth
from helpers import ParticleBinSorter
......@@ -24,6 +25,24 @@ def get_default_particle_container(numParticles=1000):
return particleContainer
class TestAllMethodsGeneric(unittest.TestCase):
allMethodClasses : list = [RandomSampling, SizeBinFractioning,
gmeth.CrossBoxSubSampling, gmeth.SpiralBoxSubsampling,
cmeth.ChemometricSubsampling]
def setUp(self) -> None:
self.methods = []
for methClass in self.allMethodClasses:
self.methods.append(methClass(None, 0.1))
def test_basic_methods