particleContainer.py 13.6 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
    
    def clearParticles(self):
        self.particles = []
    
    def clearMeasurements(self):
        self.measurements = []
47 48
        for particle in self.particles:
            particle.measurements = []
49

50 51 52 53 54 55 56
    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
57 58 59 60 61 62 63 64 65 66
    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:
67 68
            
            fname = QtWidgets.QFileDialog.getOpenFileName(QtWidgets.QWidget(), 'Select Spectra File', self.datasetParent.path, 'text file (*.txt)')[0]
JosefBrandt's avatar
JosefBrandt committed
69 70
            if fname:
                #TODO: implement a more elegant way of testing through the individual imports...
71
                try:
JosefBrandt's avatar
JosefBrandt committed
72
                    spectra, spectraNames = importSpectra.importWITecSpectra(fname)
73
                except ImportError:
JosefBrandt's avatar
JosefBrandt committed
74 75 76 77 78 79 80
                    try:
                        spectra, spectraNames = importSpectra.importRenishawSpectra(fname)
                    except ImportError:
                        try:
                            spectra, spectraNames = importSpectra.importPerkinElmerSpectra(fname)
                        except ImportError:
                            pass
81
                
JosefBrandt's avatar
JosefBrandt committed
82 83 84
            if spectra is not None:
                np.save(self.datasetParent.getSpectraFileName(), spectra)
        return spectra
85
    
JosefBrandt's avatar
JosefBrandt committed
86 87 88
    def initializeParticles(self, numParticles):
        self.particles = []
        for i in range(numParticles):
89 90 91
            newParticle = Particle()
            newParticle.index = i
            self.particles.append(newParticle)
JosefBrandt's avatar
JosefBrandt committed
92
    
93
    def setParticleContours(self, contours):
JosefBrandt's avatar
JosefBrandt committed
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):
99
        assert len(self.particles) == len(particlestats), f'numParticles = {len(self.particles)}, len partStats = {len(particlestats)}'
JosefBrandt's avatar
JosefBrandt committed
100
        for index, particle in enumerate(self.particles):
101 102
            particle.__dict__.update(particlestats[index].__dict__)
            
103
    def testForInconsistentParticleAssignments(self): #i.e., particles that have multiple measurements with different assignments
JosefBrandt's avatar
 
JosefBrandt committed
104 105 106 107 108 109 110 111 112 113 114 115 116
        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
117 118 119 120 121 122 123 124
    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
125 126 127 128
    def applyHQITresholdToParticles(self, minHQI):
        for particle in self.particles:
            particle.applyHQITresholdToMeasurements(minHQI)
            
JosefBrandt's avatar
 
JosefBrandt committed
129
    def applyAssignmentListToParticleMeasurements(self, assignmentList):
JosefBrandt's avatar
 
JosefBrandt committed
130
        '''AssignmentList is list of spectra assignments in order of spectra indices'''
JosefBrandt's avatar
JosefBrandt committed
131 132 133 134
        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
135
    
JosefBrandt's avatar
 
JosefBrandt committed
136
    def applyHQIListToParticleMeasurements(self, hqiList):
JosefBrandt's avatar
 
JosefBrandt committed
137
        '''HQI-List is list of spectra hqis in order of spectra indices'''
JosefBrandt's avatar
JosefBrandt committed
138 139 140 141
        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
142 143
    
    def getParticleOfIndex(self, index):
JosefBrandt's avatar
JosefBrandt committed
144 145
        try:
            particle = self.particles[index]
146
            assert particle.index == index, f'particle.index ({particle.index}) does not match requested index in particleList ({index})' 
JosefBrandt's avatar
JosefBrandt committed
147 148 149 150
        except:
            print('failed getting particle')
            print('requested Index:', index)
            print('len particles', len(self.particles))
151 152
            raise
        
JosefBrandt's avatar
 
JosefBrandt committed
153
        return particle
JosefBrandt's avatar
 
JosefBrandt committed
154 155 156 157 158 159
    
    def getParticleIndexContainingSpecIndex(self, index):
        for particle in self.particles:
            if index in particle.getMeasurementIndices():
                return particle.index
    
JosefBrandt's avatar
JosefBrandt committed
160 161 162
    def getNumberOfParticles(self):
        return len(self.particles)
    
JosefBrandt's avatar
JosefBrandt committed
163 164 165
    def getNumberOfMeasurements(self):
        return len(self.measurements)
    
JosefBrandt's avatar
 
JosefBrandt committed
166 167 168 169 170 171
    def getParticleContours(self):
        contours = [part.contour for part in self.particles]
        return contours
    
    def getParticleContoursByIndex(self, partIndices):
        contours = []
JosefBrandt's avatar
 
JosefBrandt committed
172 173 174
        for index in partIndices:
            particle = self.getParticleOfIndex(index)
            contours.append(particle.contour)
JosefBrandt's avatar
 
JosefBrandt committed
175 176
        return contours
    
177 178 179 180 181 182 183
    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
184 185 186 187
    def getParticleAssignmentByIndex(self, partIndex):
        particle = self.getParticleOfIndex(partIndex)
        return particle.getParticleAssignment()
    
Josef Brandt's avatar
Josef Brandt committed
188 189
    def getMeasurementPixelCoords(self) -> list:
        coords: list = []
190
        for meas in self.measurements:
191
            coords.append([meas.pixelcoord_x, meas.pixelcoord_y])
JosefBrandt's avatar
JosefBrandt committed
192
        return coords
Josef Brandt's avatar
Josef Brandt committed
193 194 195 196 197 198 199 200 201

    def getApertureCenterCoords(self) -> list:
        coords: list = []
        for particle in self.particles:
            assert particle.aperture is not None, 'Aperture for particle was not set!'
            coords.append([particle.aperture.centerX, particle.aperture.centerY])
        return coords


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

JosefBrandt's avatar
JosefBrandt committed
294
    def getTypeHistogram(self):
JosefBrandt's avatar
 
JosefBrandt committed
295
        uniquePolymers = np.unique(self.getListOfParticleAssignments())
JosefBrandt's avatar
JosefBrandt committed
296 297 298 299 300
        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)
301
        #convert back to dsetParticlecontoursict
JosefBrandt's avatar
JosefBrandt committed
302
        final_typehistogram = {i[0]: i[1] for i in sorted_typehistogram}
JosefBrandt's avatar
 
JosefBrandt committed
303
        return final_typehistogram
JosefBrandt's avatar
JosefBrandt committed
304
    
305 306 307 308 309
    def reassignParticleToAssignment(self, particleIndex, newAssignment):
        particle = self.getParticleOfIndex(particleIndex)
        particle.setAllSpectraToNewAssignment(newAssignment)
        particle.wasManuallyEdited = True
    
310 311 312
    def changeParticleColor(self, index, newColor):
        particle = self.getParticleOfIndex(index)
        particle.color = newColor
313
        particle.wasManuallyEdited = True
314
    
315 316 317
    def changeParticleShape(self, index, newShape):
        particle = self.getParticleOfIndex(index)
        particle.shape = newShape
318
        particle.wasManuallyEdited = True
319
    
JosefBrandt's avatar
JosefBrandt committed
320
    def addMergedParticle(self, particleIndices, newContour, newStats, newAssignment=None):
321
        newParticle = Particle()
322
        newParticle.contour = newContour
323 324 325 326 327 328 329
        #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)
330
                newParticle.addMeasurement(meas)
331 332

        newParticle.__dict__.update(newStats.__dict__)
333
        newParticle.wasManuallyEdited = True
JosefBrandt's avatar
 
JosefBrandt committed
334 335
        self.particles.append(newParticle)
        print('added new particle')
336
        
JosefBrandt's avatar
JosefBrandt committed
337 338 339
    def removeParticle(self, index):
        particle = self.getParticleOfIndex(index)   #just for asserting to have the correct particle!
        del self.particles[index]
340 341 342
            
    def resetParticleIndices(self):
        for newIndex, particle in enumerate(self.particles):
343
            particle.index = newIndex