particleContainer.py 13.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
# -*- 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 numpy as np
import operator
23 24 25
import os
from PyQt5 import QtWidgets

26 27
from . import importSpectra
from .particleAndMeasurement import Particle, Measurement
28

JosefBrandt's avatar
JosefBrandt committed
29
class ParticleContainer(object):
JosefBrandt's avatar
JosefBrandt committed
30
    def __init__(self, datasetParent):
JosefBrandt's avatar
 
JosefBrandt committed
31
        super(ParticleContainer, self).__init__()
JosefBrandt's avatar
JosefBrandt committed
32
        self.datasetParent = datasetParent
JosefBrandt's avatar
JosefBrandt committed
33
        self.particles = []
34
        self.measurements = []
JosefBrandt's avatar
 
JosefBrandt committed
35
        self.inconsistentParticles = []
36 37
    
    def addEmptyMeasurement(self):
JosefBrandt's avatar
JosefBrandt committed
38 39 40
        newMeas = Measurement()
        self.measurements.append(newMeas)
        return self.measurements.index(newMeas)
41 42 43 44 45 46 47
    
    def clearParticles(self):
        self.particles = []
    
    def clearMeasurements(self):
        self.measurements = []

48 49 50 51 52 53 54
    def setMeasurementScanIndex(self, indexOfMeasurment, scanIndex):
        self.measurements[indexOfMeasurment].ramanScanIndex = scanIndex
    
    def setMeasurementPixelCoords(self, indexOfMeasurment, x, y):
        self.measurements[indexOfMeasurment].pixelcoord_x = x
        self.measurements[indexOfMeasurment].pixelcoord_y = y
    
JosefBrandt's avatar
JosefBrandt committed
55 56 57 58 59 60 61 62 63 64
    def getMeasurementScanindex(self, indexOfMeasurement):
        return self.measurements[indexOfMeasurement].getScanIndex()
    
    def getSpectraFromDisk(self):
        spectra = None
        specPath = self.datasetParent.getSpectraFileName()
        
        if os.path.exists(specPath):
            spectra = np.load(specPath)
        else:
65 66
            
            fname = QtWidgets.QFileDialog.getOpenFileName(QtWidgets.QWidget(), 'Select Spectra File', self.datasetParent.path, 'text file (*.txt)')[0]
JosefBrandt's avatar
JosefBrandt committed
67 68
            if fname:
                #TODO: implement a more elegant way of testing through the individual imports...
69
                try:
JosefBrandt's avatar
JosefBrandt committed
70
                    spectra, spectraNames = importSpectra.importWITecSpectra(fname)
71
                except ImportError:
JosefBrandt's avatar
JosefBrandt committed
72 73 74 75 76 77 78
                    try:
                        spectra, spectraNames = importSpectra.importRenishawSpectra(fname)
                    except ImportError:
                        try:
                            spectra, spectraNames = importSpectra.importPerkinElmerSpectra(fname)
                        except ImportError:
                            pass
79
                
JosefBrandt's avatar
JosefBrandt committed
80 81 82
            if spectra is not None:
                np.save(self.datasetParent.getSpectraFileName(), spectra)
        return spectra
83
    
JosefBrandt's avatar
JosefBrandt committed
84 85 86
    def initializeParticles(self, numParticles):
        self.particles = []
        for i in range(numParticles):
87 88 89
            newParticle = Particle()
            newParticle.index = i
            self.particles.append(newParticle)
JosefBrandt's avatar
JosefBrandt committed
90
    
91
    def setParticleContours(self, contours):
JosefBrandt's avatar
JosefBrandt committed
92 93 94 95 96 97 98
        assert len(self.particles) == len(contours)
        for index, particle in enumerate(self.particles):
            particle.contour = contours[index]
        
    def setParticleStats(self, particlestats):
        assert len(self.particles) == len(particlestats)
        for index, particle in enumerate(self.particles):
99 100
            particle.__dict__.update(particlestats[index].__dict__)
            
101
    def testForInconsistentParticleAssignments(self): #i.e., particles that have multiple measurements with different assignments
JosefBrandt's avatar
 
JosefBrandt committed
102 103 104 105 106 107 108 109 110 111 112 113 114
        self.inconsistentParticles = []
        for particle in self.particles:
            if not particle.measurementsHaveSameOrigAssignment():
                self.inconsistentParticles.append(particle)
        if len(self.inconsistentParticles) > 0:
            print('Warning, inconsistent particles found!')
            for particle in self.inconsistentParticles:
                print(f'Particle with index {particle.index} has the following assignments:')
                for assignment in particle.getOrigMeasurementAssignments():
                    print(assignment)
        else:
            print('All particles have consistent spectra assignments')
    
JosefBrandt's avatar
 
JosefBrandt committed
115 116 117 118 119 120 121 122
    def applyPixelScaleToParticleStats(self, pixelscale):
        for index, particle in enumerate(self.particles):
            particle.longSize_box *= pixelscale
            particle.shortSize_box *= pixelscale
            particle.longSize_ellipse *= pixelscale
            particle.shortSize_ellipse *= pixelscale
            particle.area *= pixelscale**2

JosefBrandt's avatar
JosefBrandt committed
123 124 125 126
    def applyHQITresholdToParticles(self, minHQI):
        for particle in self.particles:
            particle.applyHQITresholdToMeasurements(minHQI)
            
JosefBrandt's avatar
 
JosefBrandt committed
127
    def applyAssignmentListToParticleMeasurements(self, assignmentList):
JosefBrandt's avatar
 
JosefBrandt committed
128
        '''AssignmentList is list of spectra assignments in order of spectra indices'''
JosefBrandt's avatar
JosefBrandt committed
129 130 131 132
        assert len(assignmentList) == len(self.measurements), f'assertion error in assignment of results: {len(assignmentList)} results for {len(self.measurements)} spectra...'
        for meas in self.measurements:
            scanIndex = meas.getScanIndex()
            meas.setAssignment(assignmentList[scanIndex])
JosefBrandt's avatar
 
JosefBrandt committed
133
    
JosefBrandt's avatar
 
JosefBrandt committed
134
    def applyHQIListToParticleMeasurements(self, hqiList):
JosefBrandt's avatar
 
JosefBrandt committed
135
        '''HQI-List is list of spectra hqis in order of spectra indices'''
JosefBrandt's avatar
JosefBrandt committed
136 137 138 139
        assert len(hqiList) == len(self.measurements), f'assertion error in assignment of hqis: {len(hqiList)} results for {len(self.measurements)} spectra...'
        for meas in self.measurements:
            scanIndex = meas.getScanIndex()
            meas.setHQI(hqiList[scanIndex])
JosefBrandt's avatar
 
JosefBrandt committed
140 141
    
    def getParticleOfIndex(self, index):
JosefBrandt's avatar
JosefBrandt committed
142 143 144 145 146 147
        try:
            particle = self.particles[index]
        except:
            print('failed getting particle')
            print('requested Index:', index)
            print('len particles', len(self.particles))
JosefBrandt's avatar
 
JosefBrandt committed
148 149
        assert particle.index == index, f'particle.index ({particle.index}) does match requested index in particleList ({index})' 
        return particle
JosefBrandt's avatar
 
JosefBrandt committed
150 151 152 153 154 155
    
    def getParticleIndexContainingSpecIndex(self, index):
        for particle in self.particles:
            if index in particle.getMeasurementIndices():
                return particle.index
    
JosefBrandt's avatar
JosefBrandt committed
156 157 158
    def getNumberOfParticles(self):
        return len(self.particles)
    
JosefBrandt's avatar
JosefBrandt committed
159 160 161
    def getNumberOfMeasurements(self):
        return len(self.measurements)
    
JosefBrandt's avatar
 
JosefBrandt committed
162 163 164 165 166 167
    def getParticleContours(self):
        contours = [part.contour for part in self.particles]
        return contours
    
    def getParticleContoursByIndex(self, partIndices):
        contours = []
JosefBrandt's avatar
 
JosefBrandt committed
168 169 170
        for index in partIndices:
            particle = self.getParticleOfIndex(index)
            contours.append(particle.contour)
JosefBrandt's avatar
 
JosefBrandt committed
171 172
        return contours
    
173 174 175 176 177 178 179
    def getIndicesOfColoredNotUnknownParticles(self):
        coloredParticleIndices = []
        for particle in self.particles:
            if particle.getParticleAssignment() != 'unknown' and particle.color not in ['white', 'non-determinable']:
                coloredParticleIndices.append(particle.index)
        return coloredParticleIndices
    
JosefBrandt's avatar
 
JosefBrandt committed
180 181 182 183
    def getParticleAssignmentByIndex(self, partIndex):
        particle = self.getParticleOfIndex(partIndex)
        return particle.getParticleAssignment()
    
JosefBrandt's avatar
JosefBrandt committed
184 185
    def getMeasurementPixelCoords(self):
        coords = []
186
        for meas in self.measurements:
187
            coords.append([meas.pixelcoord_x, meas.pixelcoord_y])
JosefBrandt's avatar
JosefBrandt committed
188 189
        return coords
    
JosefBrandt's avatar
 
JosefBrandt committed
190 191 192 193 194 195 196
    def getNumberOfParticlesOfAssignment(self, assignment):
        num = 0
        for particle in self.particles:
            if particle.getParticleAssignment() == assignment:
                num += 1
        return num
    
JosefBrandt's avatar
 
JosefBrandt committed
197 198 199 200 201 202 203 204
    def getNumberOfSpectraOfParticle(self, particleIndex):
        particle = self.getParticleOfIndex(particleIndex)
        return particle.getNumberOfMeasurements()

    def getSpectraIndicesOfParticle(self, particleIndex):
        particle = self.getParticleOfIndex(particleIndex)
        return particle.getMeasurementIndices()
        
JosefBrandt's avatar
JosefBrandt committed
205 206 207 208 209 210
    def getListOfParticleAssignments(self):
        particleAssignments = []
        for particle in self.particles:
            particleAssignments.append(particle.getParticleAssignment())
        return particleAssignments
    
JosefBrandt's avatar
 
JosefBrandt committed
211 212 213 214 215 216
    def getListOfHighestHQIs(self):
        hqis = []
        for particle in self.particles:
            hqis.append(particle.getHighestHQI())
        return hqis
    
JosefBrandt's avatar
 
JosefBrandt committed
217 218 219 220
    def getHQIOfSpectrumIndex(self, specIndex):
        for particle in self.particles:
            if specIndex in particle.getMeasurementIndices():
                return particle.getHQIOfMeasurementIndex(specIndex)
JosefBrandt's avatar
 
JosefBrandt committed
221 222
                    
    def getSizesOfAllParticles(self):
JosefBrandt's avatar
JosefBrandt committed
223
        particleSizes = []
JosefBrandt's avatar
 
JosefBrandt committed
224 225
        for particle in self.particles:
            particleSizes.append(particle.getParticleSize())
JosefBrandt's avatar
 
JosefBrandt committed
226
        return particleSizes
JosefBrandt's avatar
 
JosefBrandt committed
227 228 229 230
        
    def getShortSizesOfAllParticles(self):
        shortSizes = []
        for particle in self.particles:
JosefBrandt's avatar
JosefBrandt committed
231
            shortSizes.append(particle.getShortParticleSize())
JosefBrandt's avatar
 
JosefBrandt committed
232
        return shortSizes
JosefBrandt's avatar
 
JosefBrandt committed
233
    
JosefBrandt's avatar
JosefBrandt committed
234 235 236 237 238 239
    def getAreasOfAllParticles(self):
        areas = []
        for particle in self.particles:
            areas.append(particle.getArea())
        return areas
    
240 241 242 243 244 245
    def getColorsOfAllParticles(self):
        colors = []
        for particle in self.particles:
            colors.append(particle.color)
        return colors
    
246 247 248 249 250 251
    def getShapesOfAllParticles(self):
        shapes = []
        for particle in self.particles:
            shapes.append(particle.shape)
        return shapes
    
252 253 254 255
    def getParticleColorByIndex(self, particleIndex):
        particle = self.getParticleOfIndex(particleIndex)
        return particle.color
    
256 257 258 259
    def getParticleShapeByIndex(self, particleIndex):
        particle = self.getParticleOfIndex(particleIndex)
        return particle.shape
    
JosefBrandt's avatar
 
JosefBrandt committed
260 261 262 263 264
    def getSizesOfParticleType(self, assignment):
        particleSizes = []
        for particle in self.particles:
            if particle.getParticleAssignment() == assignment:
                particleSizes.append(particle.getParticleSize())
JosefBrandt's avatar
 
JosefBrandt committed
265
        return particleSizes
JosefBrandt's avatar
 
JosefBrandt committed
266
    
JosefBrandt's avatar
 
JosefBrandt committed
267 268
    def getIndicesOfParticleType(self, assignment):
        indices = []
JosefBrandt's avatar
 
JosefBrandt committed
269
        for particle in self.particles:
JosefBrandt's avatar
 
JosefBrandt committed
270 271 272 273 274 275 276
            if particle.getParticleAssignment() == assignment:
                indices.append(particle.index)
        return indices
    
    def getSizeOfParticleByIndex(self, index):
        particle = self.getParticleOfIndex(index)
        return particle.getParticleSize()
JosefBrandt's avatar
JosefBrandt committed
277
    
JosefBrandt's avatar
 
JosefBrandt committed
278 279 280 281
    def getUniquePolymers(self):
        typeHist = self.getTypeHistogram()
        return typeHist.keys()

JosefBrandt's avatar
JosefBrandt committed
282
    def getTypeHistogram(self):
JosefBrandt's avatar
 
JosefBrandt committed
283
        uniquePolymers = np.unique(self.getListOfParticleAssignments())
JosefBrandt's avatar
JosefBrandt committed
284 285 286 287 288
        typehistogram = {i: 0 for i in uniquePolymers}
        for assignment in self.getListOfParticleAssignments():
            typehistogram[assignment] += 1
        ##sort typehistogram, it will be converted into a list!!
        sorted_typehistogram = sorted(typehistogram.items(), key = operator.itemgetter(1), reverse = True)
289
        #convert back to dsetParticlecontoursict
JosefBrandt's avatar
JosefBrandt committed
290
        final_typehistogram = {i[0]: i[1] for i in sorted_typehistogram}
JosefBrandt's avatar
 
JosefBrandt committed
291
        return final_typehistogram
JosefBrandt's avatar
JosefBrandt committed
292
    
293 294 295 296 297
    def reassignParticleToAssignment(self, particleIndex, newAssignment):
        particle = self.getParticleOfIndex(particleIndex)
        particle.setAllSpectraToNewAssignment(newAssignment)
        particle.wasManuallyEdited = True
    
298 299 300
    def changeParticleColor(self, index, newColor):
        particle = self.getParticleOfIndex(index)
        particle.color = newColor
301
        particle.wasManuallyEdited = True
302
    
303 304 305
    def changeParticleShape(self, index, newShape):
        particle = self.getParticleOfIndex(index)
        particle.shape = newShape
306
        particle.wasManuallyEdited = True
307
    
JosefBrandt's avatar
JosefBrandt committed
308
    def addMergedParticle(self, particleIndices, newContour, newStats, newAssignment=None):
309
        newParticle = Particle()
310
        newParticle.contour = newContour
311 312 313 314 315 316 317
        #copy Measurements
        for index in particleIndices:
            particle = self.getParticleOfIndex(index)
            for meas in particle.getMeasurements():
                if newAssignment is not None:
                    meas.setAssignment(newAssignment)
                    meas.setHQI(100)
318
                newParticle.addMeasurement(meas)
319 320

        newParticle.__dict__.update(newStats.__dict__)
321
        newParticle.wasManuallyEdited = True
JosefBrandt's avatar
 
JosefBrandt committed
322 323
        self.particles.append(newParticle)
        print('added new particle')
324
        
JosefBrandt's avatar
JosefBrandt committed
325 326 327
    def removeParticle(self, index):
        particle = self.getParticleOfIndex(index)   #just for asserting to have the correct particle!
        del self.particles[index]
328 329 330
            
    def resetParticleIndices(self):
        for newIndex, particle in enumerate(self.particles):
331
            particle.index = newIndex