Commit a79c38ed authored by JosefBrandt's avatar JosefBrandt

Particle Color Detection implemented

Particle colors are detected automatically, but can changed through particle context menu
parent 316bc7ba
# -*- coding: utf-8 -*-
"""
GEPARD - Gepard-Enabled PARticle Detection
Copyright (C) 2018 Lars Bittrich and Josef Brandt, Leibniz-Institut für
Polymerforschung Dresden e. V. <bittrich-lars@ipfdd.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program, see COPYING.
If not, see <https://www.gnu.org/licenses/>.
"""
import pickle
def loadAssigments(fname):
with open(fname, "rb") as fp:
assignment = pickle.load(fp)
return assignment
def saveAssignments(assignment, fname):
with open(fname, "wb") as fp:
pickle.dump(assignment, fp, protocol=-1)
class DBAssignment(object):
def __init__(self):
self.filename = None
self.assignments = []
def setFileName(self, fname):
self.filename = fname
def save(self):
saveAssignments(self, self.filename)
def hasAssignment(self, polymerName):
polymIsPresent = False
for assignment in self.assignments:
if assignment.polymerName == polymerName:
polymIsPresent = True
break
return polymIsPresent
def getAssignment(self, polymerName):
for assignment in self.assignments:
if assignment.polymerName == polymerName:
return assignment
def createNewAssignment(self, polymerName):
self.assignments.append(Assignment(polymerName))
def updateAssignment(self, polymerName, result, catRes, indic_paint):
for assignment in self.assignments:
if assignment.polymerName == polymerName:
assignment.update(result, catRes, indic_paint)
return
class Assignment(object):
def __init__(self, polymerName):
self.polymerName = polymerName
self.result = None
self.categorizedResult = None
self.indication_paint = None
def update(self, result, catRes, indic_paint):
self.result = result
self.categorizedResult = catRes
self.indication_paint = indic_paint
......@@ -129,7 +129,7 @@ class SpectraPlot(QtWidgets.QGroupBox):
self.spectra = self.dataset.particleContainer.getSpectraFromDisk()
self.canvas.draw()
def updateParticleSpectrum(self, specIndex, assignment, particleSize, hqi):
def updateParticleSpectrum(self, specIndex, assignment, particleSize, hqi, color):
#draw Sample Spectrum
self.spec_axis.axis("on")
self.spec_axis.clear()
......@@ -139,7 +139,7 @@ class SpectraPlot(QtWidgets.QGroupBox):
if self.spectra is not None:
self.spec_axis.plot(self.spectra[:, 0], self.spectra[:, specIndex+1])
self.spec_axis.set_title('{}, ScanPoint Number {}, Size = {} µm, HQI = {}'.format(assignment, specIndex+1, particleSize, hqi))
self.spec_axis.set_title('{}, ScanPoint Number {}, Size = {} µm, HQI = {}, color = {}'.format(assignment, specIndex+1, particleSize, hqi, color))
self.spec_axis.set_xbound(100, (3400 if self.spectra[-1, 0] > 3400 else self.spectra[-1, 0]))
wavenumber_diff = list(self.spectra[:, 0]-100)
y_start = wavenumber_diff.index(min(wavenumber_diff))
......
......@@ -455,7 +455,8 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
particleSize = np.round(self.particleContainer.getSizeOfParticleByIndex(self.currentParticleIndex))
hqi = self.particleContainer.getHQIOfSpectrumIndex(self.currentSpectrumIndex)
assignment = self.particleContainer.getParticleAssignmentByIndex(self.currentParticleIndex)
self.specPlot.updateParticleSpectrum(self.currentSpectrumIndex, assignment, particleSize, hqi)
color = self.particleContainer.getParticleColorByIndex(self.currentParticleIndex)
self.specPlot.updateParticleSpectrum(self.currentSpectrumIndex, assignment, particleSize, hqi, color)
if self.refSelector.isEnabled() and self.refSelector.currentText() != '':
refID = self.dbWin.activeDatabase.spectraNames.index(self.refSelector.currentText())
......
......@@ -20,6 +20,7 @@ If not, see <https://www.gnu.org/licenses/>.
"""
import numpy as np
from viewitems import SegmentationContour, RamanScanIndicator
from .particleCharacterization import getParticleColor
class Particle(object):
def __init__(self):
......@@ -32,7 +33,8 @@ class Particle(object):
self.area = None
self.contour = None
self.measurements = []
self.viewItem = None
self.color = None
self.shape = None
def addMeasurement(self, refToMeasurement):
refToMeasurement.assignedParticle = self
......
......@@ -22,6 +22,7 @@ If not, see <https://www.gnu.org/licenses/>.
import numpy as np
import cv2
from copy import deepcopy
def getContourStatsWithPixelScale(cnt, pixelscale):
long, short, longellipse, shortellipse, area = getContourStats(cnt)
......@@ -42,4 +43,153 @@ def getContourStats(cnt):
area = cv2.contourArea(cnt)
return long, short, longellipse, shortellipse, area
\ No newline at end of file
return long, short, longellipse, shortellipse, area
class ColorRangeHSV(object):
def __init__(self, name, hue, hue_tolerance, min_sat, max_sat):
self.name = name
self.minHue = hue-hue_tolerance/2
self.maxHue = hue+hue_tolerance/2
self.minSat = min_sat
self.maxSat = max_sat
def containsHSV(self, hsv):
hue = hsv[0]
sat = hsv[1]
if self.minHue <= hue <= self.maxHue and self.minSat <= sat <= self.maxSat:
return True
else:
if self.name != 'white':
return False
else:
if sat < 128 and hsv[2] > 70:
return True
class ColorClassifier(object):
def __init__(self):
hue_tolerance = 30
self.colors = [ColorRangeHSV('yellow', 30, hue_tolerance, 30, 255),
ColorRangeHSV('blue', 120, hue_tolerance, 80, 255),
ColorRangeHSV('red', 180, hue_tolerance, 50, 255),
ColorRangeHSV('red', 0, hue_tolerance, 50, 255),
ColorRangeHSV('green', 70, hue_tolerance, 50, 255),
ColorRangeHSV('white', 128, 256, 0, 50)]
def classifyColor(self, meanHSV):
result = 'non-determinable'
for color in self.colors:
if color.containsHSV(meanHSV):
result = color.name
break
return result
def getParticleColor(imgRGB, colorClassifier=None):
img = cv2.cvtColor(imgRGB, cv2.COLOR_RGB2HSV_FULL)
meanHSV = cv2.mean(img)
if colorClassifier is None:
colorClassifier = ColorClassifier()
color = colorClassifier.classifyColor(meanHSV)
return color
def mergeContours(contours):
img, xmin, ymin, padding = contoursToImg(contours)
return imgToCnt(img, xmin, ymin, padding)
def getParticleImageFromFullimage(contour, fullimage):
contourCopy = deepcopy(contour)
xmin, xmax, ymin, ymax = getContourExtrema(contourCopy)
img = fullimage[ymin:ymax, xmin:xmax]
mask = np.zeros(img.shape[:2])
for i in range(len(contourCopy)):
contourCopy[i][0][0] -= xmin
contourCopy[i][0][1] -= ymin
cv2.drawContours(mask, [contourCopy], -1, (255, 255, 255), -1)
cv2.drawContours(mask, [contourCopy], -1, (255, 255, 255), 1)
img[mask == 0] = 0
img = np.array(img, dtype = np.uint8)
return img
def contoursToImg(contours, padding=2):
contourCopy = deepcopy(contours)
xmin, xmax, ymin, ymax = getContourExtrema(contourCopy)
padding = padding #pixel in each direction
rangex = int(np.round((xmax-xmin)+2*padding))
rangey = int(np.round((ymax-ymin)+2*padding))
img = np.zeros((rangey, rangex))
for curCnt in contourCopy:
for i in range(len(curCnt)):
curCnt[i][0][0] -= xmin-padding
curCnt[i][0][1] -= ymin-padding
cv2.drawContours(img, [curCnt], -1, 255, -1)
cv2.drawContours(img, [curCnt], -1, 255, 1)
img = np.uint8(cv2.morphologyEx(img, cv2.MORPH_CLOSE, np.ones((3, 3))))
return img, xmin, ymin, padding
def imgToCnt(img, xmin, ymin, padding):
def getSimpleContour(img):
if cv2.__version__ > '3.5':
contour, hierarchy = cv2.findContours(img, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)
else:
temp, contour, hierarchy = cv2.findContours(img, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)
if len(contour)>1:
raise NotConnectedContoursError
return contour
def getFullContour(img):
if cv2.__version__ > '3.5':
contour, hierarchy = cv2.findContours(img, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE)
else:
temp, contour, hierarchy = cv2.findContours(img, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE)
if len(contour)>1:
raise NotConnectedContoursError
return contour
contour = getSimpleContour(img)
if len(contour[0]) < 5:
contour = getFullContour(img)
newContour = contour[0]
for i in range(len(newContour )):
newContour [i][0][0] += xmin-padding
newContour [i][0][1] += ymin-padding
return newContour
def getContourExtrema(contours):
try:
cnt = np.vstack(tuple(contours))
xmin, xmax = cnt[:,0,:][:, 0].min(), cnt[:,0,:][:, 0].max()
ymin, ymax = cnt[:,0,:][:, 1].min(), cnt[:,0,:][:, 1].max()
except IndexError: #i.e., not a list of contours was passed, but an individual contour. Hence, the above indexing does not work
xmin, xmax = cnt[:, 0].min(), cnt[:, 0].max()
ymin, ymax = cnt[:, 1].min(), cnt[:, 1].max()
return xmin, xmax, ymin, ymax
class NotConnectedContoursError(Exception):
pass
if __name__ == '__main__':
colors = {'white': (41, 25, 66),
"red": (128, 121, 57),
"red2": (23, 88, 49),
"yellow": (25, 121, 91),
"pink": (11, 79, 51),
"brown": (32, 38, 64),
"green": (54, 99, 53)}
classifier= ColorClassifier()
# print(classifier.hsv)
for name, mean in colors.items():
print(name, classifier.classifyColor(mean))
\ No newline at end of file
......@@ -248,6 +248,16 @@ class ParticleContainer(object):
areas.append(particle.getArea())
return areas
def getColorsOfAllParticles(self):
colors = []
for particle in self.particles:
colors.append(particle.color)
return colors
def getParticleColorByIndex(self, particleIndex):
particle = self.getParticleOfIndex(particleIndex)
return particle.color
def getSizesOfParticleType(self, assignment):
particleSizes = []
for particle in self.particles:
......@@ -281,6 +291,10 @@ class ParticleContainer(object):
final_typehistogram = {i[0]: i[1] for i in sorted_typehistogram}
return final_typehistogram
def changeParticleColor(self, index, newColor):
particle = self.getParticleOfIndex(index)
particle.color = newColor
def addMergedParticle(self, particleIndices, newContour, newStats, newAssignment=None):
newParticle = Particle()
#copy Measurements
......
# -*- coding: utf-8 -*-
"""
Created on Wed Jan 16 12:43:00 2019
@author: brandt
"""
"""
GEPARD - Gepard-Enabled PARticle Detection
Copyright (C) 2018 Lars Bittrich and Josef Brandt, Leibniz-Institut für
......@@ -26,19 +20,17 @@ If not, see <https://www.gnu.org/licenses/>.
"""
import numpy as np
import cv2
from PyQt5 import QtWidgets, QtCore
from copy import deepcopy
from .particlePainter import ParticlePainter
from .particleCharacterization import getContourStatsWithPixelScale
import analysis.particleCharacterization as pc
class ParticleContextMenu(QtWidgets.QMenu):
combineParticlesSignal = QtCore.pyqtSignal(list, str)
reassignParticlesSignal = QtCore.pyqtSignal(list, str)
paintParticlesSignal = QtCore.pyqtSignal(list, str)
changeParticleColorSignal = QtCore.pyqtSignal(list, str)
deleteParticlesSignal = QtCore.pyqtSignal(list)
def __init__(self, viewparent):
......@@ -83,22 +75,33 @@ class ParticleContextMenu(QtWidgets.QMenu):
elif numParticles == 1:
self.combineMenu.setDisabled(True)
self.colorMenu = QtWidgets.QMenu("Set Particle Color To")
self.colorActs = []
for color in ['white', 'black', 'blue', 'brown', 'green', 'grey', 'non-determinable', 'red', 'transparent', 'yellow']:
self.colorActs.append(self.colorMenu.addAction(color))
infoAct = self.addAction(f'selected {numParticles} particles')
infoAct.setDisabled(True)
self.addMenu(self.combineMenu)
self.addMenu(self.reassignMenu)
self.addMenu(self.paintMenu)
self.addMenu(self.colorMenu)
self.deleteAct = self.addAction("Delete particle(s)")
action = self.exec_(screenPos)
if action:
newAssignment = self.validifyAssignment(action.text())
actionText = self.validifyAssignment(action.text())
if action in self.combineActs:
self.combineParticlesSignal.emit(self.selectedParticleIndices, newAssignment)
self.combineParticlesSignal.emit(self.selectedParticleIndices, actionText)
elif action in self.reassignActs:
self.reassignParticlesSignal.emit(self.selectedParticleIndices, newAssignment)
self.reassignParticlesSignal.emit(self.selectedParticleIndices, actionText)
elif action in self.paintActs:
self.paintParticlesSignal.emit(self.selectedParticleIndices, newAssignment)
self.paintParticlesSignal.emit(self.selectedParticleIndices, actionText)
elif action in self.colorActs:
self.changeParticleColorSignal.emit(self.selectedParticleIndices, actionText)
elif action == self.deleteAct:
self.deleteParticlesSignal.emit(self.selectedParticleIndices)
......@@ -130,6 +133,7 @@ class ParticleEditor(QtCore.QObject):
contextMenu.combineParticlesSignal.connect(self.combineParticles)
contextMenu.reassignParticlesSignal.connect(self.reassignParticles)
contextMenu.paintParticlesSignal.connect(self.paintParticles)
contextMenu.changeParticleColorSignal.connect(self.changeParticleColors)
contextMenu.deleteParticlesSignal.connect(self.deleteParticles)
def createSafetyBackup(self):
......@@ -146,12 +150,14 @@ class ParticleEditor(QtCore.QObject):
print(f'Combining particles {contourIndices} into {newAssignment}')
contours = self.particleContainer.getParticleContoursByIndex(contourIndices)
try:
newContour = self.mergeContours(contours)
except NotConnectedContoursError:
newContour = pc.mergeContours(contours)
except pc.NotConnectedContoursError:
QtWidgets.QMessageBox.critical(self.viewparent, 'ERROR!',
'Particle contours are not connected or have holes.\nThat is currently not supported!')
return
pixelscale = self.viewparent.dataset.getPixelScale()
stats = getContourStatsWithPixelScale(newContour, pixelscale)
stats = pc.getContourStatsWithPixelScale(newContour, pixelscale)
self.mergeParticlesInParticleContainerAndSampleView(contourIndices,newContour, stats, newAssignment)
......@@ -172,9 +178,7 @@ class ParticleEditor(QtCore.QObject):
self.storedAssignmend = newAssignment
contours = self.particleContainer.getParticleContoursByIndex(contourIndices)
# topLeft = self.getTopLeft(contours)
# img, self.xmin, self.ymin, self.padding = self.contoursToImg(contours, padding=0)
img, xmin, ymin, self.padding = self.contoursToImg(contours, padding=0)
img, xmin, ymin, self.padding = pc.contoursToImg(contours, padding=0)
topLeft = [ymin, xmin]
self.particlePainter = ParticlePainter(self, img, topLeft)
......@@ -188,15 +192,17 @@ class ParticleEditor(QtCore.QObject):
img = self.particlePainter.img
xmin = self.particlePainter.topLeft[1]
ymin = self.particlePainter.topLeft[0]
newContour = self.imgToCnt(img, xmin, ymin, 0)
except NotConnectedContoursError:
newContour = pc.imgToCnt(img, xmin, ymin, 0)
except pc.NotConnectedContoursError:
QtWidgets.QMessageBox.critical(self.viewparent, 'ERROR!',
'Particle contours are not connected or have holes.\nThat is currently not supported!')
self.storedIndices = []
self.storedAssignmend = None
self.destroyParticlePainter()
return
pixelscale = self.viewparent.dataset.getPixelScale()
stats = getContourStatsWithPixelScale(newContour, pixelscale)
stats = pc.getContourStatsWithPixelScale(newContour, pixelscale)
self.mergeParticlesInParticleContainerAndSampleView(self.storedIndices, newContour, stats, self.storedAssignmend)
......@@ -211,57 +217,6 @@ class ParticleEditor(QtCore.QObject):
self.viewparent.update()
self.particlePainter = None
def mergeContours(self, contours):
img, xmin, ymin, padding = self.contoursToImg(contours)
return self.imgToCnt(img, xmin, ymin, padding)
def contoursToImg(self, contours, padding=2):
contourCopy = deepcopy(contours)
cnt = np.vstack(tuple(contourCopy)) #combine contous
#draw contours
xmin, xmax = cnt[:,0,:][:, 0].min(), cnt[:,0,:][:, 0].max()
ymin, ymax = cnt[:,0,:][:, 1].min(), cnt[:,0,:][:, 1].max()
padding = padding #pixel in each direction
rangex = int(np.round((xmax-xmin)+2*padding))
rangey = int(np.round((ymax-ymin)+2*padding))
img = np.zeros((rangey, rangex))
for curCnt in contourCopy:
for i in range(len(curCnt)):
curCnt[i][0][0] -= xmin-padding
curCnt[i][0][1] -= ymin-padding
cv2.drawContours(img, [curCnt], -1, 255, -1)
cv2.drawContours(img, [curCnt], -1, 255, 1)
img = np.uint8(cv2.morphologyEx(img, cv2.MORPH_CLOSE, np.ones((3, 3))))
return img, xmin, ymin, padding
def imgToCnt(self, img, xmin, ymin, padding):
if cv2.__version__ > '3.5':
contours, hierarchy = cv2.findContours(img, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE)
else:
temp, contours, hierarchy = cv2.findContours(img, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE)
if len(contours)>1:
QtWidgets.QMessageBox.critical(self.viewparent, 'ERROR!',
'Particle contours are not connected or have holes.\nThat is currently not supported!')
raise NotConnectedContoursError
newContour = contours[0]
for i in range(len(newContour)):
newContour[i][0][0] += xmin-padding
newContour[i][0][1] += ymin-padding
return newContour
def getTopLeft(self, contours):
cnt = np.vstack(tuple(contours)) #combine contous
#draw contours
xmin = cnt[:,0,:][:, 0].min()
ymin= cnt[:,0,:][:, 1].min()
return [ymin, xmin]
def mergeParticlesInParticleContainerAndSampleView(self, indices, newContour, stats, assignment):
self.viewparent.addParticleContourToIndex(newContour, len(self.viewparent.contourItems)-1)
self.particleContainer.addMergedParticle(indices, newContour, stats, newAssignment=assignment)
......@@ -275,6 +230,12 @@ class ParticleEditor(QtCore.QObject):
self.particleAssignmentChanged.emit()
#TODO: INCLUDE SANITY CHECK!!!!!!!!!
@QtCore.pyqtSlot(list, str)
def changeParticleColors(self, contourIndices, newColor):
print(f'changing color of particles {contourIndices} into {newColor}')
for partIndex in contourIndices:
self.particleContainer.changeParticleColor(partIndex, newColor)
@QtCore.pyqtSlot(list)
def deleteParticles(self, contourIndices):
reply = QtWidgets.QMessageBox.question(self, f'About to delete {len(contourIndices)} particles.',
......@@ -291,7 +252,6 @@ class ParticleEditor(QtCore.QObject):
self.viewparent.analysiswidget.updateHistogramsAndContours()
class NotConnectedContoursError(Exception):
pass
\ No newline at end of file
......@@ -41,6 +41,7 @@ class SQLExport(QtWidgets.QDialog):
self.longSizes = np.round(self.particleContainer.getSizesOfAllParticles())
self.shortSize = np.round(self.particleContainer.getShortSizesOfAllParticles())
self.colors = self.particleContainer.getColorsOfAllParticles()
self.spectra = self.particleContainer.getSpectraFromDisk()
self.particleImages = None
......@@ -217,6 +218,7 @@ class SQLExport(QtWidgets.QDialog):
usedCols['Amount'] = str(1)
usedCols['Analyst'] = str(self.analystIndices[self.analystSelector.currentIndex()])
usedCols['Size_fraction'] = self.getSizeFraction(sizeCategories, self.longSizes[polymInd])
usedCols['Colour'] = self.colors[polymInd]
usedCols[sizeCols[0]] = str(self.longSizes[polymInd])
usedCols[sizeCols[1]] = str(self.shortSize[polymInd])
if self.particleImages is not None:
......
......@@ -25,8 +25,9 @@ import cv2
from helperfunctions import cv2imread_fix, cv2imwrite_fix
from copy import copy
from analysis.particleContainer import ParticleContainer
from legacyConvert import legacyConversion
currentversion = 3
currentversion = 4
def loadData(fname):
retds = None
......@@ -39,7 +40,7 @@ def loadData(fname):
retds.version = 0
retds.__dict__.update(ds.__dict__)
if retds.version < currentversion:
retds.legacyConversion()
legacyConversion(retds)
elif retds.zvalimg=="saved":
retds.loadZvalImg()
return retds
......@@ -173,113 +174,7 @@ class DataSet(object):
def loadZvalImg(self):
if os.path.exists(self.getZvalImageName()):
self.zvalimg = cv2imread_fix(self.getZvalImageName(), cv2.IMREAD_GRAYSCALE)
def legacyConversion(self, recreatefullimage=False):
if self.version==0:
print("Converting legacy version 0 to 1")
print("This may take some time")
# local imports as these functions are only needed for the rare occasion of legacy conversion
from opticalscan import loadAndPasteImage
# try to load png and check for detection contours
recreatefullimage = recreatefullimage or not os.path.exists(self.getLegacyImageName())
if not recreatefullimage:
img = cv2imread_fix(self.getLegacyImageName())
Nc = len(self.particlecontours)
if Nc>0:
contour = self.particlecontours[Nc//2]
contpixels = img[contour[:,0,1],contour[:,0,0]]
if np.all(contpixels[:,1]==255) and np.all(contpixels[:,2]==0) \
and np.all(contpixels[:,0]==0):
recreatefullimage = True
if not recreatefullimage:
cv2imwrite_fix(self.getImageName(), img)
del img
if recreatefullimage:
print("recreating fullimage from grid data")
imgdata = None
zvalimg = None
Ngrid = len(self.grid)
width, height, rotationvalue = self.imagedim_df
p0, p1 = self.maxdim[:2], self.maxdim[2:]
for i in range(Ngrid):
print(f"Processing image {i+1} of {Ngrid}")
names = []
for k in range(len(self.zpositions)):
names.append(os.path.join(self.getScanPath(), f"image_{i}_{k}.bmp"))
p = self.grid[i]
imgdata, zvalimg = loadAndPasteImage(names, imgdata, zvalimg, width,
height, rotationvalue, p0, p1, p)
self.zvalimg = zvalimg
cv2imwrite_fix(self.getImageName(), cv2.cvtColor(imgdata, cv2.COLOR_RGB2BGR))
del imgdata
self.saveZvalImg()
if "particleimgs" in self.__dict__:
del self.particleimgs
self.version = 1
if self.version == 1:
print("Converting legacy version 1 to 2")
if hasattr(self, 'pixelscale'):
print('pixelscale was', self.pixelscale)
self.pixelscale_bf = self.pixelscale
self.pixelscale_df = self.pixelscale
del self.pixelscale
if hasattr(self, 'imagedim'):
self.imagedim_bf = self.imagedim
self.imagedim_df = self.imagedim
del self.imagedim
if not hasattr(self, 'particles2spectra'):
self.particles2spectra = [[int(np.where(self.ramanscansortindex == i)[0])] for i in range(len(self.ramanscansortindex))]
self.version = 2
if self.version == 2:
print("Converting legacy version 2 to 3")
self.particleContainer = ParticleContainer(self)
self.particleContainer.initializeParticles(len(self.particlestats))
self.particleContainer.setParticleContours(self.particlecontours)
self.particleContainer.setParticleStats(self.particlestats)
self.particleContainer.applyPixelScaleToParticleStats(self.getPixelScale())
self.particleContainer.clearMeasurements()
if not hasattr(self, 'particles2spectra') or self.particles2spectra is None:
self.particles2spectra = [[int(np.where(self.ramanscansortindex == i)[0])] for i in range(len(self.ramanscansortindex))]
if len(self.particlestats) > 0: #i.e., particle detection was completed and particle data is there
self.particleDetectionDone = True
for particleIndex, listOfScanIndices in enumerate(self.particles2spectra):
curParticle = self.particleContainer.getParticleOfIndex(particleIndex)
curParticle.measurements = []
for scanIndex in listOfScanIndices:
indexOfNewMeas = self.particleContainer.addEmptyMeasurement()
indexOfMeasInRamanPoints = self.ramanscansortindex[scanIndex]
x, y = self.ramanpoints[indexOfMeasInRamanPoints][0], self.ramanpoints[indexOfMeasInRamanPoints][1]
self.particleContainer.setMeasurementPixelCoords(indexOfNewMeas, x, y)
self.particleContainer.setMeasurementScanIndex(indexOfNewMeas, scanIndex)
curParticle.addMeasurement(self.particleContainer.measurements[indexOfNewMeas])
if hasattr(self, 'results') and self.results['polymers'] is not None: #transfer results
for particle in self.particleContainer.particles:
for meas in particle.measurements:
specIndex = meas.getScanIndex()
meas.setAssignment(self.results['polymers'][specIndex])
meas.setHQI(self.results['hqis'][specIndex])
self.particleContainer.testForInconsistentParticles()
self.version = 3
# add later conversion for higher version numbers here
def getSubImage(self, img, index, draw=True):
contour = self.particlecontours[index]
x0, x1 = contour[:,0,0].min(), contour[:,0,0].max()
......@@ -378,15 +273,9 @@ class DataSet(object):
def getLegacyImageName(self):
return os.path.join(self.path, "fullimage.png")
def getLegacyDetectImageName(self):
return os.path.join(self.path, "detectimage.png")
def getBackgroundImageName(self):
return os.path.join(self.path, "background.bmp")
def getDetectImageName(self):
raise NotImplementedError("No longer implemented due to change in API")
def getTmpImageName(self):
return os.path.join(self.path, "tmp.bmp")
......
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
GEPARD - Gepard-Enabled PARticle Detection
Copyright (C) 2018 Lars Bittrich and Josef Brandt, Leibniz-Institut für
Polymerforschung Dresden e. V. <bittrich-lars@ipfdd.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.