Commit 1911d4e7 authored by Robert's avatar Robert Committed by ohmacht@zwei-g.de

-create square particles, where gepard did not find a contour matching a ps measurement

-assign 'not yet measured' on contours found by gepard, that do not contain a ps measurement
parent 4fcd0f47
...@@ -75,6 +75,12 @@ class ParticleContainer(object): ...@@ -75,6 +75,12 @@ class ParticleContainer(object):
particle.measurements = [] particle.measurements = []
def setMeasurementScanIndex(self, indexOfMeasurment, scanIndex): def setMeasurementScanIndex(self, indexOfMeasurment, scanIndex):
"""
associates measurment/contour/particle with scan result via its (scan) index
:param indexOfMeasurment:
:param scanIndex:
:return:
"""
self.measurements[indexOfMeasurment].specScanIndex = scanIndex self.measurements[indexOfMeasurment].specScanIndex = scanIndex
def setMeasurementPixelCoords(self, indexOfMeasurment, x, y): def setMeasurementPixelCoords(self, indexOfMeasurment, x, y):
......
...@@ -219,6 +219,7 @@ class DataSet(object): ...@@ -219,6 +219,7 @@ class DataSet(object):
self.resultsUploadedToSQL = [] self.resultsUploadedToSQL = []
self.seedParticles = None self.seedParticles = None
self.contourSeedMapping = None
self.stickToSeedPoints = False self.stickToSeedPoints = False
self.readin = True # a value that is always set to True at loadData self.readin = True # a value that is always set to True at loadData
......
...@@ -29,7 +29,8 @@ import matplotlib.pyplot as plt ...@@ -29,7 +29,8 @@ import matplotlib.pyplot as plt
from threading import Thread from threading import Thread
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from ..segmentation import Segmentation from ..particlescoutimporter import ParticleScoutImporter
from ..segmentation import Segmentation, MeasurementPoint
from ..analysis.particleCharacterization import getParticleStatsWithPixelScale, loadZValImageFromDataset from ..analysis.particleCharacterization import getParticleStatsWithPixelScale, loadZValImageFromDataset
from ..errors import InvalidParticleError, showErrorMessageAsWidget from ..errors import InvalidParticleError, showErrorMessageAsWidget
from .uielements import TimeEstimateProgressbar from .uielements import TimeEstimateProgressbar
...@@ -37,6 +38,7 @@ from ..scenePyramid import ScenePyramid ...@@ -37,6 +38,7 @@ from ..scenePyramid import ScenePyramid
from ..gepardlogging import setDefaultLoggingConfig from ..gepardlogging import setDefaultLoggingConfig
from ..helperfunctions import needsFTIRAperture, positionWidgetOnScreen from ..helperfunctions import needsFTIRAperture, positionWidgetOnScreen
if TYPE_CHECKING: if TYPE_CHECKING:
from ..sampleview import SampleView from ..sampleview import SampleView
from .viewItemHandler import ViewItemHandler from .viewItemHandler import ViewItemHandler
...@@ -848,32 +850,46 @@ class ParticleDetectionView(QtWidgets.QWidget): ...@@ -848,32 +850,46 @@ class ParticleDetectionView(QtWidgets.QWidget):
if self.dataset.stickToSeedPoints: if self.dataset.stickToSeedPoints:
self.dataset.specscandone = True self.dataset.specscandone = True
# remove all detected particles that do not contain seedpoints
finalContours = [] # contour index and all associated seedpoints/spectra
finalPoints = [] contourSeedMapping = {}
# used seedpoints
usedSeeds = [] usedSeeds = []
# check contours # for all remaining contours
for i, c in enumerate(contours): for i, c in enumerate(contours):
# for having a seedpoint # check for having a/more than one seedpoint
hasSeedPoint = False
for j, p in enumerate(self.dataset.seedpoints): for j, p in enumerate(self.dataset.seedpoints):
# inside of or on them # inside of or on them
hasSeedPoint = hasSeedPoint or 0 <= cv2.pointPolygonTest(c, (p[0], p[1]), False) if 0 <= cv2.pointPolygonTest(c, (p[0], p[1]), False):
if hasSeedPoint: # found, record list of seedpoint indices for every contour index
# found, save index if i not in contourSeedMapping:
contourSeedMapping[i] = []
contourSeedMapping[i].append(j)
usedSeeds.append(j) usedSeeds.append(j)
# next
break
if hasSeedPoint: # seedpoints, that did not get detected by gepard
# seedpoint found inside contour unusedSeeds = [j for j in range(self.dataset.seedpoints.shape[0]) if j not in usedSeeds]
finalContours.append(c) if 0 < len(unusedSeeds):
finalPoints.append(measurementPoints[i]) # add oddly 'shaped' contour at these seedpoints
for i in unusedSeeds:
p = self.dataset.seedpoints[i]
idx = len(contours)
contours.append(
ParticleScoutImporter.notFoundByGepardStamp(
p,
self.img.shape[1],
self.img.shape[0]
)
)
measurementPoints.append([MeasurementPoint(idx, p[0], p[1])])
usedSeeds.append(i)
if idx not in contourSeedMapping:
contourSeedMapping[idx] = []
contourSeedMapping[idx].append(i)
# order seed particles and spectra by measurement/contour order # order seed particles and spectra by measurement/contour order
self.dataset.seedParticles = [self.dataset.seedParticles[i] for i in usedSeeds] #self.dataset.seedParticles = [self.dataset.seedParticles[i] for i in usedSeeds]
contours = finalContours self.dataset.contourSeedMapping = contourSeedMapping
measurementPoints = finalPoints
particlestats, invalidParticleIndices = self.getParticleStats(contours) particlestats, invalidParticleIndices = self.getParticleStats(contours)
contours = removeInvalidContours(contours, invalidParticleIndices) contours = removeInvalidContours(contours, invalidParticleIndices)
...@@ -905,9 +921,9 @@ class ParticleDetectionView(QtWidgets.QWidget): ...@@ -905,9 +921,9 @@ class ParticleDetectionView(QtWidgets.QWidget):
ftir: bool = needsFTIRAperture(self.view) ftir: bool = needsFTIRAperture(self.view)
for contourIndex, contour in enumerate(contours): for contourIndex, contour in enumerate(contours):
try: try:
stats = getParticleStatsWithPixelScale(
stats = getParticleStatsWithPixelScale(contour, self.dataset, fullimage=self.img, zimg=zvalimg, contour, self.dataset, fullimage=self.img, zimg=zvalimg, ftir=ftir
ftir=ftir) )
except InvalidParticleError: except InvalidParticleError:
self.logger.debug('Invalid contour in detection, skipping particle.') self.logger.debug('Invalid contour in detection, skipping particle.')
invalidParticleIndices.append(contourIndex) invalidParticleIndices.append(contourIndex)
......
...@@ -138,7 +138,8 @@ class SpecScanUI(QtWidgets.QWidget): ...@@ -138,7 +138,8 @@ class SpecScanUI(QtWidgets.QWidget):
# if imported from particle scout # if imported from particle scout
if self.dataset.stickToSeedPoints: if self.dataset.stickToSeedPoints:
spectra = [] spectra = []
# particles are in the same order as the measurements/spectra # collect all particle scout spectra
# some of them may be located in the same contour/measurement/particle
for i, p in enumerate(self.dataset.seedParticles): for i, p in enumerate(self.dataset.seedParticles):
# first particle # first particle
if 0 == len(spectra): if 0 == len(spectra):
...@@ -146,8 +147,34 @@ class SpecScanUI(QtWidgets.QWidget): ...@@ -146,8 +147,34 @@ class SpecScanUI(QtWidgets.QWidget):
spectra = np.array([np.array(p['spectrum'])[:, 0]]) spectra = np.array([np.array(p['spectrum'])[:, 0]])
# get particle spectrum # get particle spectrum
spectra = np.concatenate((spectra, np.array([np.array(p['spectrum'])[:, 1]]))) spectra = np.concatenate((spectra, np.array([np.array(p['spectrum'])[:, 1]])))
# associate spectrum idx with measurement
self.dataset.particleContainer.setMeasurementScanIndex(i, i) # @todo: add 'virtual' scan index to measurements without data from particle scout
for contourIdx, seedpoints in self.dataset.contourSeedMapping.items():
# associate spectrum idx with measurement/particle/contour
# in some rare cases, multiple particle scout scan results lie within one gepard contour
# take first scan result and inform via console
if 1 < len(seedpoints):
materials = [p['material'] for p in [self.dataset.seedParticles[i] for i in seedpoints]]
self.logger.info(
f'we have more than one spectrum for a contour ({",".join(materials)}), taking first'
)
self.dataset.particleContainer.setMeasurementScanIndex(
contourIdx,
seedpoints[0]# + 1 # +1 because first spectra rec is the header
)
# if gepard detected more particles/contours, than particle scout provided
# check for measurements without spectrum association
diff = 0
base = spectra.shape[0] - 1 # without -1 bc first rec is the header
sl = len(p['spectrum'])
for m in self.dataset.particleContainer.measurements:
if np.isnan(m.getScanIndex()):
# add 'virtual' spectrum and associate measurement with it
spectra = np.concatenate((spectra, [np.zeros(sl, dtype=np.int)]))
m.specScanIndex = base + diff
diff += 1
# write spectrum file # write spectrum file
# 2d array with # 2d array with
# num(cols) = num(particle/measurements/spectra) # num(cols) = num(particle/measurements/spectra)
...@@ -160,9 +187,18 @@ class SpecScanUI(QtWidgets.QWidget): ...@@ -160,9 +187,18 @@ class SpecScanUI(QtWidgets.QWidget):
fname = os.path.join(self.dataset.path, f'{self.dataset.name}_TrueMatchResults.txt') fname = os.path.join(self.dataset.path, f'{self.dataset.name}_TrueMatchResults.txt')
with open(fname, 'w') as fp: with open(fname, 'w') as fp:
fp.write("Search Spectrum Name;HQI 1;Found Spectrum Name 1;IsMarked;\n") fp.write("Search Spectrum Name;HQI 1;Found Spectrum Name 1;IsMarked;\n")
for i, p in enumerate(self.dataset.seedParticles): for contourIdx, seedpoints in self.dataset.contourSeedMapping.items():
i = seedpoints[0]
p = self.dataset.seedParticles[i]
fp.write(f"Spectrum {i+1};100;{p['material']};yes;\n") fp.write(f"Spectrum {i+1};100;{p['material']};yes;\n")
if 0 < diff:
# add virtual spectra
for v in range(diff):
fp.write(f"Spectrum {base + v + 1};100;not yet measured;yes;\n")
self.dataset.stickToSeedPoints = False
@QtCore.pyqtSlot() @QtCore.pyqtSlot()
def cancelScan(self): def cancelScan(self):
if self.process is not None and self.process.is_alive(): if self.process is not None and self.process.is_alive():
......
...@@ -35,6 +35,37 @@ if TYPE_CHECKING: # avoids cyclic imports due to type hints.. ...@@ -35,6 +35,37 @@ if TYPE_CHECKING: # avoids cyclic imports due to type hints..
from .sampleview import SampleView from .sampleview import SampleView
class ParticleScoutImporter: class ParticleScoutImporter:
@staticmethod
def notFoundByGepardStamp(p, maxX, maxY):
'''
returns square with side length of 300px, clipped at img edges, with center at p
:param p:
:param maxX:
:param maxY:
:return:
'''
sideLength = 100
xmin = int(p[0] - sideLength / 2)
if 0 > xmin:
xmin = 0
xmax = int(p[0] + sideLength / 2)
if maxX < xmax:
xmax = maxX
ymin = int(p[1] - sideLength / 2)
if 0 > ymin:
ymin = 0
ymax = int(p[1] + sideLength / 2)
if maxY < ymax:
ymax = maxY
return np.array([
[[xmin, ymin]], [[xmax, ymin]], [[xmax, ymax]], [[xmin, ymax]]
], dtype=np.int_)
def __init__(self, fname, parent=None): def __init__(self, fname, parent=None):
self.parent: SampleView = parent self.parent: SampleView = parent
self.dataSet = None self.dataSet = None
...@@ -117,7 +148,7 @@ class ParticleScoutImporter: ...@@ -117,7 +148,7 @@ class ParticleScoutImporter:
coords = {} coords = {}
with open(os.path.join(self.path, f"{self.basename}.txt"), 'r') as fp: with open(os.path.join(self.path, f"{self.basename}.txt"), 'r') as fp:
for line in fp: for line in fp:
match = re.match(r'^\s*(?P<var>[^:]+):\s*(?P<value>[0-9-,]+)$', line) match = re.match(r'^\s*(?P<var>[^:]+):\s*(?P<value>[0-9-.,]+)$', line)
if match: if match:
coords[match.group('var').lower()] = float(match.group('value').replace(',', '.')) coords[match.group('var').lower()] = float(match.group('value').replace(',', '.'))
...@@ -186,6 +217,12 @@ class ParticleScoutImporter: ...@@ -186,6 +217,12 @@ class ParticleScoutImporter:
return missing, merged return missing, merged
def buildDataSet(self, coords, particles): def buildDataSet(self, coords, particles):
'''
computes parameters from given particle scout data
:param coords: coordinate system parameters given in txt file
:param particles:
:return:
'''
fname = QtWidgets.QFileDialog.getSaveFileName( fname = QtWidgets.QFileDialog.getSaveFileName(
self.parent, self.parent,
'Create New GEPARD Project', 'Create New GEPARD Project',
...@@ -223,6 +260,8 @@ class ParticleScoutImporter: ...@@ -223,6 +260,8 @@ class ParticleScoutImporter:
self.dataSet.readin = False self.dataSet.readin = False
# set image center as reference point (assume just one tile) # set image center as reference point (assume just one tile)
# in upright coord system the given coords point to center point of image # in upright coord system the given coords point to center point of image
centerPointGiven = False
if centerPointGiven:
coords['y'] += coords['height'] / 2 coords['y'] += coords['height'] / 2
coords['x'] -= coords['width'] / 2 coords['x'] -= coords['width'] / 2
# in upright coord system now the coords point to upper left point of image # in upright coord system now the coords point to upper left point of image
......
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