particleContainer.py 12.8 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

29

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

51 52 53 54 55 56 57
    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
58 59 60 61 62 63 64 65 66 67
    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:
68 69
            
            fname = QtWidgets.QFileDialog.getOpenFileName(QtWidgets.QWidget(), 'Select Spectra File', self.datasetParent.path, 'text file (*.txt)')[0]
JosefBrandt's avatar
JosefBrandt committed
70 71
            if fname:
                #TODO: implement a more elegant way of testing through the individual imports...
72
                try:
JosefBrandt's avatar
JosefBrandt committed
73
                    spectra, spectraNames = importSpectra.importWITecSpectra(fname)
74
                except ImportError:
JosefBrandt's avatar
JosefBrandt committed
75 76 77 78 79 80 81
                    try:
                        spectra, spectraNames = importSpectra.importRenishawSpectra(fname)
                    except ImportError:
                        try:
                            spectra, spectraNames = importSpectra.importPerkinElmerSpectra(fname)
                        except ImportError:
                            pass
82
                
JosefBrandt's avatar
JosefBrandt committed
83 84 85
            if spectra is not None:
                np.save(self.datasetParent.getSpectraFileName(), spectra)
        return spectra
86
    
JosefBrandt's avatar
JosefBrandt committed
87 88 89
    def initializeParticles(self, numParticles):
        self.particles = []
        for i in range(numParticles):
90 91 92
            newParticle = Particle()
            newParticle.index = i
            self.particles.append(newParticle)
JosefBrandt's avatar
JosefBrandt committed
93
    
94
    def setParticleContours(self, contours):
JosefBrandt's avatar
JosefBrandt committed
95 96 97 98 99 100 101
        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):
102 103
            particle.__dict__.update(particlestats[index].__dict__)
            
104
    def testForInconsistentParticleAssignments(self): #i.e., particles that have multiple measurements with different assignments
JosefBrandt's avatar
 
JosefBrandt committed
105 106 107 108 109 110 111 112 113 114 115 116 117
        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
118 119 120 121 122 123 124 125
    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
126 127 128 129
    def applyHQITresholdToParticles(self, minHQI):
        for particle in self.particles:
            particle.applyHQITresholdToMeasurements(minHQI)
            
JosefBrandt's avatar
 
JosefBrandt committed
130
    def applyAssignmentListToParticleMeasurements(self, assignmentList):
JosefBrandt's avatar
 
JosefBrandt committed
131
        '''AssignmentList is list of spectra assignments in order of spectra indices'''
JosefBrandt's avatar
JosefBrandt committed
132 133 134 135
        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
136
    
JosefBrandt's avatar
 
JosefBrandt committed
137
    def applyHQIListToParticleMeasurements(self, hqiList):
JosefBrandt's avatar
 
JosefBrandt committed
138
        '''HQI-List is list of spectra hqis in order of spectra indices'''
JosefBrandt's avatar
JosefBrandt committed
139 140 141 142
        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
143 144
    
    def getParticleOfIndex(self, index):
JosefBrandt's avatar
JosefBrandt committed
145 146 147 148 149 150
        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
151 152
        assert particle.index == index, f'particle.index ({particle.index}) does match requested index in particleList ({index})' 
        return particle
JosefBrandt's avatar
 
JosefBrandt committed
153 154 155 156 157 158
    
    def getParticleIndexContainingSpecIndex(self, index):
        for particle in self.particles:
            if index in particle.getMeasurementIndices():
                return particle.index
    
JosefBrandt's avatar
JosefBrandt committed
159 160 161
    def getNumberOfParticles(self):
        return len(self.particles)
    
JosefBrandt's avatar
JosefBrandt committed
162 163 164
    def getNumberOfMeasurements(self):
        return len(self.measurements)
    
JosefBrandt's avatar
 
JosefBrandt committed
165 166 167 168 169 170
    def getParticleContours(self):
        contours = [part.contour for part in self.particles]
        return contours
    
    def getParticleContoursByIndex(self, partIndices):
        contours = []
JosefBrandt's avatar
 
JosefBrandt committed
171 172 173
        for index in partIndices:
            particle = self.getParticleOfIndex(index)
            contours.append(particle.contour)
JosefBrandt's avatar
 
JosefBrandt committed
174 175
        return contours
    
JosefBrandt's avatar
 
JosefBrandt committed
176 177 178 179
    def getParticleAssignmentByIndex(self, partIndex):
        particle = self.getParticleOfIndex(partIndex)
        return particle.getParticleAssignment()
    
JosefBrandt's avatar
JosefBrandt committed
180 181
    def getMeasurementPixelCoords(self):
        coords = []
182
        for meas in self.measurements:
183
            coords.append([meas.pixelcoord_x, meas.pixelcoord_y])
JosefBrandt's avatar
JosefBrandt committed
184 185
        return coords
    
JosefBrandt's avatar
 
JosefBrandt committed
186 187 188 189 190 191 192
    def getNumberOfParticlesOfAssignment(self, assignment):
        num = 0
        for particle in self.particles:
            if particle.getParticleAssignment() == assignment:
                num += 1
        return num
    
JosefBrandt's avatar
 
JosefBrandt committed
193 194 195 196 197 198 199 200
    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
201 202 203 204 205 206
    def getListOfParticleAssignments(self):
        particleAssignments = []
        for particle in self.particles:
            particleAssignments.append(particle.getParticleAssignment())
        return particleAssignments
    
JosefBrandt's avatar
 
JosefBrandt committed
207 208 209 210 211 212
    def getListOfHighestHQIs(self):
        hqis = []
        for particle in self.particles:
            hqis.append(particle.getHighestHQI())
        return hqis
    
JosefBrandt's avatar
 
JosefBrandt committed
213 214 215 216
    def getHQIOfSpectrumIndex(self, specIndex):
        for particle in self.particles:
            if specIndex in particle.getMeasurementIndices():
                return particle.getHQIOfMeasurementIndex(specIndex)
JosefBrandt's avatar
 
JosefBrandt committed
217 218
                    
    def getSizesOfAllParticles(self):
JosefBrandt's avatar
JosefBrandt committed
219
        particleSizes = []
JosefBrandt's avatar
 
JosefBrandt committed
220 221
        for particle in self.particles:
            particleSizes.append(particle.getParticleSize())
JosefBrandt's avatar
 
JosefBrandt committed
222
        return particleSizes
JosefBrandt's avatar
 
JosefBrandt committed
223 224 225 226
        
    def getShortSizesOfAllParticles(self):
        shortSizes = []
        for particle in self.particles:
JosefBrandt's avatar
JosefBrandt committed
227
            shortSizes.append(particle.getShortParticleSize())
JosefBrandt's avatar
 
JosefBrandt committed
228
        return shortSizes
JosefBrandt's avatar
 
JosefBrandt committed
229
    
JosefBrandt's avatar
JosefBrandt committed
230 231 232 233 234 235
    def getAreasOfAllParticles(self):
        areas = []
        for particle in self.particles:
            areas.append(particle.getArea())
        return areas
    
236 237 238 239 240 241
    def getColorsOfAllParticles(self):
        colors = []
        for particle in self.particles:
            colors.append(particle.color)
        return colors
    
242 243 244 245 246 247
    def getShapesOfAllParticles(self):
        shapes = []
        for particle in self.particles:
            shapes.append(particle.shape)
        return shapes
    
248 249 250 251
    def getParticleColorByIndex(self, particleIndex):
        particle = self.getParticleOfIndex(particleIndex)
        return particle.color
    
252 253 254 255
    def getParticleShapeByIndex(self, particleIndex):
        particle = self.getParticleOfIndex(particleIndex)
        return particle.shape
    
JosefBrandt's avatar
 
JosefBrandt committed
256 257 258 259 260
    def getSizesOfParticleType(self, assignment):
        particleSizes = []
        for particle in self.particles:
            if particle.getParticleAssignment() == assignment:
                particleSizes.append(particle.getParticleSize())
JosefBrandt's avatar
 
JosefBrandt committed
261
        return particleSizes
JosefBrandt's avatar
 
JosefBrandt committed
262
    
JosefBrandt's avatar
 
JosefBrandt committed
263 264
    def getIndicesOfParticleType(self, assignment):
        indices = []
JosefBrandt's avatar
 
JosefBrandt committed
265
        for particle in self.particles:
JosefBrandt's avatar
 
JosefBrandt committed
266 267 268 269 270 271 272
            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
273
    
JosefBrandt's avatar
 
JosefBrandt committed
274 275 276 277
    def getUniquePolymers(self):
        typeHist = self.getTypeHistogram()
        return typeHist.keys()

JosefBrandt's avatar
JosefBrandt committed
278
    def getTypeHistogram(self):
JosefBrandt's avatar
 
JosefBrandt committed
279
        uniquePolymers = np.unique(self.getListOfParticleAssignments())
JosefBrandt's avatar
JosefBrandt committed
280 281 282 283 284
        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)
285
        #convert back to dsetParticlecontoursict
JosefBrandt's avatar
JosefBrandt committed
286
        final_typehistogram = {i[0]: i[1] for i in sorted_typehistogram}
JosefBrandt's avatar
 
JosefBrandt committed
287
        return final_typehistogram
JosefBrandt's avatar
JosefBrandt committed
288
    
289 290 291 292 293
    def reassignParticleToAssignment(self, particleIndex, newAssignment):
        particle = self.getParticleOfIndex(particleIndex)
        particle.setAllSpectraToNewAssignment(newAssignment)
        particle.wasManuallyEdited = True
    
294 295 296
    def changeParticleColor(self, index, newColor):
        particle = self.getParticleOfIndex(index)
        particle.color = newColor
297
        particle.wasManuallyEdited = True
298
    
299 300 301
    def changeParticleShape(self, index, newShape):
        particle = self.getParticleOfIndex(index)
        particle.shape = newShape
302
        particle.wasManuallyEdited = True
303
    
JosefBrandt's avatar
JosefBrandt committed
304
    def addMergedParticle(self, particleIndices, newContour, newStats, newAssignment=None):
305
        newParticle = Particle()
306
        newParticle.contour = newContour
307 308 309 310 311 312 313
        #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)
314
                newParticle.addMeasurement(meas)
315 316

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