Commit c438bcca authored by Hackmet's avatar Hackmet

Peparation for RenishawCom, inclusion of analysis module

parent bddc18b2
......@@ -21,7 +21,7 @@ Requirements:
for 64bit as many use cases require a lot of memory (16 GB better 32 GB
recommended)
* the tsp module in externalmodules can be built with
* the tsp module in external can be built with
python setuptsp.py
please note: for this step a valid compiler needs to be installed in the
system; Otherwise use the precompiled tsp-module
......
# -*- 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/>.
"""
from PyQt5 import QtCore, QtWidgets, QtGui
from PIL import ImageFont
import numpy as np
WX, WY = 1024, 200
class Legend(QtWidgets.QMdiSubWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint|QtCore.Qt.FramelessWindowHint)
self.setFixedSize(200, 800)
self.wscale = 1
self.drag = None
self.items = [] #list of items to display (text, color)
self.tileSize = 12
self.fontSize = 15
self.spacer = 10
def mousePressEvent(self, event):
if event.button()==QtCore.Qt.LeftButton:
self.drag = event.pos()
def mouseMoveEvent(self, event):
if self.drag is not None:
p0 = event.pos()
self.move(self.mapToParent(p0-self.drag))
self.parentWidget().update()
else:
super().mouseMoveEvent(event)
def mouseReleaseEvent(self, event):
self.drag = None
super().mouseReleaseEvent(event)
def paintEvent(self, event):
numEntries = len(self.items)
if numEntries > 0:
def getSize(fontsize, text, tileSize, spacer):
# font = ImageFont.truetype('arial.ttf', fontsize)
# size = font.getsize(text)
size = 5*len(text), fontsize+2
width, height = size[0]*1.5 + tileSize + spacer, numEntries * (tileSize+1*spacer) + 2*spacer
return width, height
fontSize, tileSize, spacer = self.fontSize, self.tileSize, self.spacer
longestEntry = max([i[0] for i in self.items], key=len)
width, height = getSize(fontSize, longestEntry, tileSize, spacer)
#scale smaller, if necessary
if height > 1024:
factor = 1024/height
#prevent text getting tooo small:
factor = np.clip(factor, 0.6, 1) #0.6*15 would be fontSize 9
height, tileSize, fontSize, spacer = height*factor, tileSize*factor, int(fontSize*factor), spacer*factor
width, height = getSize(fontSize, longestEntry, tileSize, spacer)
self.setFixedSize(width, height)
qp = QtGui.QPainter()
qp.begin(self)
font = QtGui.QFont()
font.setPixelSize(fontSize)
qp.setFont(font)
for index, item in enumerate(self.items):
#draw Text
x0 = tileSize+2*spacer
y0 = index*(fontSize+spacer)
rect= QtCore.QRectF(x0, y0, width, float(fontSize+spacer))
qp.drawText(rect, QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter, item[0])
#draw colored Box
qp.setBrush(item[1])
x0 = spacer
y0 = index*(fontSize+spacer) + (fontSize - tileSize)/2 +spacer/2
qp.drawRect(x0, y0, tileSize, tileSize)
qp.end()
\ No newline at end of file
This diff is collapsed.
# -*- 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
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 cv2
#import matplotlib.pyplot as plt
class ParticleEditor(object):
def __init__(self, parent):
self.parent = parent #the assigned analysis widget
def createSafetyBackup(self):
self.parent.parent.dataset.saveBackup()
def combineParticles(self, contourIndices, new_assignment):
contourIndices = sorted(contourIndices) #we want to keep the contour with lowest index
print('selected contours:', contourIndices)
self.createSafetyBackup()
#get contours:
contours = [self.parent.parent.dataset.particlecontours[i] for i in contourIndices]
cnt = np.vstack(tuple(contours)) #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 = 2 #pixel in each direction
rangex = int(np.round((xmax-xmin)+2*padding))
rangey = int(np.round((ymax-ymin)+2*padding))
for i in range(len(cnt)):
cnt[i][0][0] -= xmin-padding
cnt[i][0][1] -= ymin-padding
img = np.zeros((rangey, rangex))
cv2.drawContours(img, [cnt], 0, 1, -1)
cv2.drawContours(img, [cnt], 0, 1, 1)
img = np.uint8(cv2.morphologyEx(img, cv2.MORPH_CLOSE, np.ones((3, 3))))
temp, contours, hierarchy = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
newContour = contours[0]
stats = self.characterizeParticle(newContour)
for i in range(len(newContour)):
newContour[i][0][0] += xmin-padding
newContour[i][0][1] += ymin-padding
#check, if dataset contains (already modified) particle2spectra, otherwise create new.
if self.parent.parent.dataset.particles2spectra is None: #create default assignment
print('recreating particles2spectra from within edit particles...')
sortindices = self.parent.parent.dataset.ramanscansortindex
self.parent.parent.dataset.particles2spectra = [[int(np.where(sortindices == i)[0])] for i in range(len(sortindices))]
#Contour indices are the same as the original particlestats, which are contained in the dataset.
#We have to modify that and reload in the analysisview
#first, overwrite first index with new particlestats
self.parent.parent.dataset.particlestats[contourIndices[0]] = stats
#now, delete the rest...
self.parent.parent.dataset.particlestats = [i for ind, i in enumerate(self.parent.parent.dataset.particlestats) if ind not in contourIndices[1:]]
#same with the contours
self.parent.parent.dataset.particlecontours[contourIndices[0]] = newContour
self.parent.parent.dataset.particlecontours = [i for ind, i in enumerate(self.parent.parent.dataset.particlecontours) if ind not in contourIndices[1:]]
#update particle2spectra_list
#what is the current particle index??
specIndices = []
#other spectra indices:
for index in contourIndices:
specIndices.append(self.parent.particles2spectra[index])
#flatten index list (in case, that a nested list was created...)
specIndices = list(np.unique(np.array(specIndices)))
for i in specIndices:
self.parent.spectraResults[i] = new_assignment
self.parent.hqis[i] = 100 #avoid sorting them out again by hqi-filter...
print(f'spectrum {i} of particle{contourIndices[0]} is now {new_assignment}')
#modify particles2spectra..
self.parent.parent.dataset.particles2spectra[contourIndices[0]] = specIndices
for index in reversed(contourIndices[1:]):
print('removing index from particles2spectra:', index)
del self.parent.parent.dataset.particles2spectra[index]
#save dataset
self.parent.parent.dataset.save()
#update contours in sampleview
self.parent.parent.contouritem.resetContours(self.parent.parent.dataset.particlecontours)
self.parent.loadParticleData()
def reassignParticles(self, contourindices, new_assignment):
self.createSafetyBackup()
for partIndex in contourindices:
for specIndex in self.parent.particles2spectra[partIndex]:
self.parent.currentPolymers[specIndex] = new_assignment
self.parent.spectraResults[specIndex] = new_assignment
self.parent.hqis[specIndex] = 100
self.parent.createHistogramData()
def deleteParticles(self):
self.createSafetyBackup()
pass
def splitParticles(self):
self.createSafetyBackup()
pass
def characterizeParticle(self, contours):
##characterize particle
longellipse, shortellipse = np.nan, np.nan
cnt = contours
if cnt.shape[0] >= 5: ##at least 5 points required for ellipse fitting...
ellipse = cv2.fitEllipse(cnt)
shortellipse, longellipse = ellipse[1]
rect = cv2.minAreaRect(cnt)
long, short = rect[1]
if short>long:
long, short = short, long
return long, short, longellipse, shortellipse, cv2.contourArea(cnt)
#if __name__ == '__main__':
# import
\ No newline at end of file
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Thu May 31 10:07:45 2018
@author: brandt
"""
import numpy as np
from PyQt5 import QtWidgets
import sys
from os import chdir, getcwd
class LoadWITecResults(QtWidgets.QDialog):
def __init__(self, parent):
super(LoadWITecResults, self).__init__()
self.setGeometry(400, 400, 200, 300)
self.setWindowTitle('Get Truematch Results')
self.layout = QtWidgets.QGridLayout()
self.setLayout(self.layout)
self.parent = parent
self.parent.setDisabled(True)
self.trueMatchResults = None
self.polymertypes = None
self.additives = None
self.hqis = None
self.addhqis = None
self.btn1 = QtWidgets.QPushButton('LoadTrueMatchResults')
self.btn1.resize(self.btn1.sizeHint())
self.btn1.clicked.connect(self.loadFileManually)
optionsLayout = QtWidgets.QFormLayout()
self.optionsGroup = QtWidgets.QGroupBox('Compute Options')
self.optionsGroup.setDisabled(True)
self.btn3 = QtWidgets.QPushButton('Compute')
self.btn3.clicked.connect(self.runCalculations)
self.label1 = QtWidgets.QLabel('HQI-Threshold:')
self.spinbox1 = QtWidgets.QDoubleSpinBox(self)
self.spinbox1.valueChanged.connect(self.updateParentSpinboxes)
optionsLayout.addRow(self.label1, self.spinbox1)
self.label2 = QtWidgets.QLabel('ComponentThreshold:')
self.spinbox2 = QtWidgets.QDoubleSpinBox(self)
self.spinbox2.valueChanged.connect(self.updateParentSpinboxes)
optionsLayout.addRow(self.label2, self.spinbox2)
self.label3 = QtWidgets.QLabel('Max Items in Display:')
self.numDispSpinbox = QtWidgets.QSpinBox(self)
self.numDispSpinbox.setValue(20)
self.numDispSpinbox.setMinimum(1)
self.numDispSpinbox.setMaximum(40)
self.numDispSpinbox.valueChanged.connect(self.updateParentSpinboxes)
optionsLayout.addRow(self.label3, self.numDispSpinbox)
optionsLayout.addRow(self.btn3)
for box in [self.spinbox1, self.spinbox2]:
box.setValue(50)
box.setDecimals(1)
box.setSingleStep(5)
box.setMinimum(0)
box.setMaximum(100)
self.spinbox2.setValue(30) #more reasonable...
self.optionsGroup.setLayout(optionsLayout)
self.reviewGroup = QtWidgets.QGroupBox('Review Changes')
self.reviewGroup.setDisabled(True)
reviewLayout = QtWidgets.QVBoxLayout()
self.otherBtn = QtWidgets.QPushButton('manual overwrite')
self.otherBtn.clicked.connect(self.show3FlagsReview)
reviewLayout.addWidget(self.otherBtn)
reviewLayout.addStretch()
self.reviewGroup.setLayout(reviewLayout)
self.layout.addWidget(self.btn1, 0, 0)
self.layout.addWidget(self.optionsGroup, 1, 0)
self.layout.addWidget(self.reviewGroup, 2, 0)
self.manualPolymers = {}
self.manualAdditives = {}
self.expWindow = None
self.additivePlot = None
self.editEntryWindow = None
self.numFlagForTakeSpectrum = 1
self.numFlagForSetUnknown = 2
self.numFlagForPrompt = 3
def updateParentSpinboxes(self):
self.parent.hqiSpinBox.setValue(self.spinbox1.value())
self.parent.compHqiSpinBox.setValue(self.spinbox2.value())
self.parent.dispResultSpinBox.setValue(self.numDispSpinbox.value())
def show3FlagsReview(self):
self.editEntryWindow = ModifyManualEdits(self, self.manualPolymers, self.manualAdditives)
self.editEntryWindow.show()
def loadFileManually(self):
dsetpath = self.parent.parent.dataset.path
fnames =QtWidgets.QFileDialog.getOpenFileNames(self, 'Select TrueMatch result file', dsetpath, 'text file (*.txt)')[0]
if len(fnames) > 1:
QtWidgets.QMessageBox.about(self, 'Info', 'The following order of files was loaded. If incorrect, please call a coder!\n{}'.format('\n'.join([fname for fname in fnames])))
self.trueMatchResults = []
for fileindex, fname in enumerate(fnames):
with open(fname) as file:
if fileindex == 0:
for line in file:
self.trueMatchResults.append(line)
else: ##for additional files skip first line (header..)
for lineindex, line in enumerate(file):
if lineindex > 0:
self.trueMatchResults.append(line)
self.btn1.setText('Data loaded')
self.optionsGroup.setDisabled(False)
def formatResults(self, rawResults): #get rid of header line, first data interpretation
results = []
for index,line in enumerate(rawResults):
if index == 0:
line = line.strip().split(';')[:-1] #disregard the last entry of each line, as each line ends with ; <- that produces an empty entry...
if line[0] != 'Search Spectrum Name':
print('incompatible data format')
break
#detect, whether one- or multicomponent-search was done
if line[-1] == 'IsMarked':
numhits = np.int(line[-2].split(' ')[-1])
numcomps = 1
else:
numhits = np.int(line[-1].split(' ')[-1])
numcomps = np.int(line[-1].split(' ')[-3])
else:
results.append(line)
numspectra = len(results)
if numspectra > 0:
print('{} components, {} hits per sample, {} spectra'.format(numcomps, numhits, numspectra))
return results, numspectra, numcomps, numhits
def interpretEntry(self, index, entry, numhits, numcomps):
entry = entry.split(';')
polymertype, additive, hqi, addhqi = None, None, None, None #assign default None
#find yes-flags
flags = np.where(np.array(entry) == 'yes')[0]
if len(flags) == 0:
#take highest HQI entry
if numcomps == 1:
if float(entry[1]) > self.spinbox1.value():
polymertype = entry[2]
hqi = entry[1]
else:
polymertype = 'unknown'
hqi = 0
else:
if float(entry[1]) > self.spinbox1.value():
polymertype = entry[5]
hqi = entry[1]
if float(entry[6]) > self.spinbox2.value():
additive = entry[7]
addhqi = entry[6]
else:
additive = 'none'
addhqi = 0
else:
polymertype = 'unknown'
additive = 'none'
hqi = 0
addhqi = 0
elif len(flags) == 1:
#exactly one flag was placed, take this entry
if numcomps == 1:
polymertype = entry[int(flags[0])-1]
hqi = 100
else:
polymertype = entry[int(flags[0])+2]
additive = entry[int(flags[0])+4]
hqi = 100
addhqi = 100
elif len(flags) == 2:
polymertype = 'unknown'
hqi = 0
if numcomps > 1:
additive = 'none'
addhqi = 0
elif len(flags) == 3:
hqi = 100
if index not in self.manualPolymers:
polymertype, ok = QtWidgets.QInputDialog.getText(self, 'Name of main component', 'Spectrum at index {} is:'.format(index))
self.manualPolymers[index] = polymertype
else:
polymertype = self.manualPolymers[index]
if numcomps > 1:
addhqi = 100
if entry[0] not in self.manualAdditives:
additive, ok = QtWidgets.QInputDialog.getText(self, 'Name of additive', 'Additive at index {} is:'.format(index))
self.manualAdditives[index] = additive
else:
additive = self.manualAdditives[index]
else:
QtWidgets.QMessageBox.about(self, 'Error!', 'No rule for {} flags, found at spectrum index {}'.format(len(flags), index))
if addhqi is None:
addhqi = 0
return polymertype, additive, float(hqi), float(addhqi)
def runCalculations(self):
self.resultList, numspectra, numcomps, numhits = self.formatResults(self.trueMatchResults)
self.dispresults = self.numDispSpinbox.value()
self.polymertypes =[]
self.hqis = []
if numcomps == 1: #####SINGLE COMPONENT SEARCH
self.additives = None
self.addhqis = None
self.spinbox2.setEnabled(False)
for index, entry in enumerate(self.resultList):
if len(entry) == 0:
del self.resultList[index]
else:
polymertype, additive, hqi, addhqi = self.interpretEntry(index, entry, numhits, numcomps)
self.polymertypes.append(polymertype)
self.hqis.append(hqi)
else: #####MULTI-COMPONENT SEARCH
self.additives = []
self.addhqis = []
for index, entry in enumerate(self.resultList):
if len(entry) > 0:
polymertype, additive, hqi = self.interpretEntry(index, entry, numhits, numcomps)
self.polymertypes.append(polymertype)
self.hqis.append(hqi)
self.additives.append(additive)
self.addhqis.append(addhqi)
assert len(self.polymertypes) == len(self.resultList), 'incorrect number of polymer types added...'
del self.parent.spectraResults, self.parent.additiveResults, self.parent.hqis, self.parent.addhqis
self.parent.spectraResults = self.polymertypes
self.parent.additiveResults = self.additives
self.parent.hqis = self.hqis
self.parent.addhqis = self.addhqis
self.parent.formatResults()
if len(self.manualPolymers) > 0:
self.reviewGroup.setDisabled(False)
def closeEvent(self, event):
del self.parent.spectraResults, self.parent.additiveResults, self.parent.hqis, self.parent.addhqis
self.parent.spectraResults = self.polymertypes
self.parent.additiveResults = self.additives
self.parent.hqis = self.hqis
self.parent.addhqis = self.addhqis
self.parent.updateBtn.clicked.connect(self.parent.formatResults)
self.parent.formatResults()
self.parent.show_hide_labels()
self.parent.saveAnalysisResults()
self.parent.setEnabled(True)
event.accept()
class ModifyManualEdits(QtWidgets.QWidget):
def __init__(self, parentWindow, polymerEdits, additiveEdits = None):
super(ModifyManualEdits, self).__init__()
self.setWindowTitle('Edit Manual Changes')
self.setGeometry(200, 200, 800, 500)
layout = QtWidgets.QVBoxLayout()
self.setLayout(layout)
self.parent = parentWindow
self.polymerEdits = polymerEdits
self.additiveEdits = additiveEdits
self.labels = []
self.polymerLineEdits = []
self.additiveLineEdits = []
groupBox = QtWidgets.QGroupBox('Manually entered edits')
groupLayout = QtWidgets.QGridLayout()
#create Labels and LineEdits
for index in self.polymerEdits:
self.labels.append(QtWidgets.QLabel('Spectrum index:' + str(index)))
print(self.labels[-1].text())
self.polymerLineEdits.append(QtWidgets.QLineEdit())
self.polymerLineEdits[-1].setText(self.polymerEdits[index])
if len(self.additiveEdits) > 0:
self.additiveLineEdits.append(QtWidgets.QLineEdit())
self.additiveLineEdits[-1].setText(self.additiveEdits[index])
for i in range(len(self.labels)):
groupLayout.addWidget(self.labels[i], i, 0)
groupLayout.addWidget(self.polymerLineEdits[i], i, 1)
if len(self.additiveEdits) > 0:
groupLayout.addWidget(self.additiveLineEdits[i], i, 2)
groupBox.setLayout(groupLayout)
</