Commit a6fa4f0e authored by JosefBrandt's avatar JosefBrandt

No further ContourApproximation + Code Cleanup

Contour approximation can lead to wrong ellipse fits...
parent c90f7b26
......@@ -45,7 +45,6 @@ except:
class ParticleAnalysis(QtWidgets.QMainWindow):
def __init__(self, dataset, viewparent=None):
super(ParticleAnalysis, self).__init__(viewparent)
# self.resize(1680, 1050)
self.setWindowTitle('Results of polymer analysis')
self.layout = QtWidgets.QHBoxLayout()
self.widget = QtWidgets.QWidget()
......@@ -599,15 +598,4 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
window.close()
self.viewparent.imparent.particelAnalysisAct.setChecked(False)
event.accept()
if __name__ == '__main__':
from ..dataset import DataSet
def run():
app = QtWidgets.QApplication(sys.argv)
meas = ParticleAnalysis(DataSet("dummydata"))
meas.showMaximized()
return app.exec_()
run()
\ No newline at end of file
event.accept()
\ No newline at end of file
......@@ -27,8 +27,7 @@ with permission from github user CJ Carey (perimosocordiae)
from PyQt5 import QtWidgets, QtCore
import numpy as np
import sys
import dill
import dill #TODO: Make it run with pickle... Having two different methods is not so sensefull...
import os
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
......@@ -317,16 +316,14 @@ class DataBaseWindow(QtWidgets.QMainWindow):
self.removeSpecBtn.setText('Remove {}'.format(self.activeSpectrumName))
self.removeSpecBtn.setDisabled(False)
def save(self):
def save(self, showMessage=True):
for index, db in enumerate(self.databases):
# pickling_on = open(db.title+'.dbpkl', 'wb')
# pickle.dump(db, pickling_on)
# pickling_on.close()
savename = os.path.join(self.path, db.title+'.db')
with open(savename, 'wb') as f:
dill.dump(db, f)
QtWidgets.QMessageBox.about(self, 'Done.', 'Saved {} database(s)'.format(len(self.databases)))
if showMessage:
QtWidgets.QMessageBox.about(self, 'Done.', 'Saved {} database(s)'.format(len(self.databases)))
def updateDBSelectorList(self):
self.db_selector.clear()
......@@ -339,7 +336,6 @@ class DataBaseWindow(QtWidgets.QMainWindow):
self.db_selector.setCurrentText(self.activeDatabase.title)
def selectDataBase(self, refreshParent=False):
# if not self.noDBFound:
if len(self.databases) > 0:
self.activeDatabaseIndex = self.db_selector.currentIndex()
self.activeDatabase = self.databases[self.activeDatabaseIndex]
......@@ -352,13 +348,7 @@ class DataBaseWindow(QtWidgets.QMainWindow):
self.parent.populateRefSelector()
def closeEvent(self, event):
# response = QtWidgets.QMessageBox.question(self, 'Warning', 'Exit without saving?', QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, QtWidgets.QMessageBox.No)
# if response == QtWidgets.QMessageBox.Yes:
# if self.parent is not None:
# self.parent.setDisabled(False)
# event.accept()
# else:
# event.ignore()
self.save(showMessage=False)
if self.parent is not None:
self.parent.setDisabled(False)
......@@ -426,8 +416,6 @@ class BaseLineCorrect(QtWidgets.QDialog):
for i in range(len(spectra)):
spectra[i][:, 1] = self.origspectra[i][:, 1].copy() - self.als_baseline(self.origspectra[i][:, 1].copy(), asymmetry_param=self.asymSpinBox.value(),
smoothness_param=self.smoothSpinBox.value(), max_iters = self.iterSpinBox.value())
# self.newSpectra = spectra.copy()
# self.parent.drawSpectrum(spectra[self.parent.activeSpectrumIndex], self.parent.activeSpectrumName)
self.saveBtn.setDisabled(False)
def als_baseline(self, intensities, asymmetry_param=0.05, smoothness_param=1e4,
......@@ -453,27 +441,17 @@ class BaseLineCorrect(QtWidgets.QDialog):
if conv < conv_thresh:
break
w = new_w
# else:
# print( 'ALS did not converge in %d iterations' % max_iters)
else:
print( 'ALS did not converge in %d iterations' % max_iters)
return z
def save(self):
# self.parent.databases[self.parent.activeDatabaseIndex].spectra = self.spectra
# self.parent.activeDatabase = self.parent.databases[self.parent.activeDatabaseIndex]
# self.parent.activeSpectrum = self.parent.activeDatabase.spectra[self.parent.activeSpectrumIndex]
# self.parent.drawSpectrum(self.parent.activeSpectrum, self.parent.activeSpectrumName)
self.close()
def cancel(self):
self.close()
def closeEvent(self, event):
if self.parent is not None:
self.parent.setDisabled(False)
# self.parent.drawSpectrum(self.parent.activeSpectrum, self.parent.activeSpectrumName)
class WhittakerSmoother(object):
def __init__(self, signal, smoothness_param, deriv_order=1):
......@@ -549,18 +527,4 @@ class CropSpectra(QtWidgets.QDialog):
def closeEvent(self, event):
if self.parent is not None:
self.parent.setDisabled(False)
event.accept()
def main():
global dbWin
#start Application
app = QtWidgets.QApplication(sys.argv)
dbWin = DataBaseWindow(None)
app.exec_()
if __name__ == '__main__':
main()
\ No newline at end of file
event.accept()
\ No newline at end of file
......@@ -30,14 +30,12 @@ class ExpExcelDialog(QtWidgets.QDialog):
super(ExpExcelDialog, self).__init__()
self.setWindowTitle('Export Options')
self.setGeometry(200, 200, 300, 300)
self.layout = QtWidgets.QHBoxLayout()
self.setLayout(self.layout)
self.dataset = dataset
self.particleContainer = dataset.particleContainer
self.layout = QtWidgets.QHBoxLayout()
self.setLayout(self.layout)
excelvbox = QtWidgets.QVBoxLayout()
excelvbox.addWidget(QtWidgets.QLabel('Select Parameters for Export'))
excelgroup = QtWidgets.QGroupBox("Export to Excel", self)
......
......@@ -45,8 +45,8 @@ def particleIsValid(particle):
return False
return True
def getParticleStatsWithPixelScale(cnt, fullimage, dataset):
def getParticleStatsWithPixelScale(contour, fullimage, dataset):
cnt = deepcopy(contour)
pixelscale = dataset.getPixelScale()
newStats = ParticleStats()
......@@ -64,7 +64,7 @@ def getParticleStatsWithPixelScale(cnt, fullimage, dataset):
newStats.longSize, newStats.shortSize = getFibreDimension(cnt)
newStats.longSize *= pixelscale
newStats.shortSize *= pixelscale
partImg = getParticleImageFromFullimage(cnt, fullimage)
newStats.color = getParticleColor(partImg)
return newStats
......@@ -76,8 +76,6 @@ def getFibreDimension(contour):
maxThickness = np.max(dist)*2
return longSize, maxThickness
def getParticleColor(imgRGB, colorClassifier=None):
img = cv2.cvtColor(imgRGB, cv2.COLOR_RGB2HSV_FULL)
meanHSV = cv2.mean(img)
......@@ -130,6 +128,7 @@ def getParticleImageFromFullimage(contour, fullimage):
xmin, xmax, ymin, ymax = getContourExtrema(contourCopy)
img = fullimage[ymin:ymax, xmin:xmax]
img = img.copy()
mask = np.zeros(img.shape[:2])
for i in range(len(contourCopy)):
......@@ -164,11 +163,11 @@ def contoursToImg(contours, padding=0):
return img, xmin, ymin, padding
def imgToCnt(img, xmin, ymin, padding=0):
def getContour(img, flag):
def getContour(img, contourMode):
if cv2.__version__ > '3.5':
contours, hierarchy = cv2.findContours(img, cv2.RETR_EXTERNAL, flag)
contours, hierarchy = cv2.findContours(img, cv2.RETR_EXTERNAL, contourMode)
else:
temp, contours, hierarchy = cv2.findContours(img, cv2.RETR_EXTERNAL, flag)
temp, contours, hierarchy = cv2.findContours(img, cv2.RETR_EXTERNAL, contourMode)
if len(contours) == 0: #i.e., no contour found
raise InvalidParticleError
......@@ -188,10 +187,7 @@ def imgToCnt(img, xmin, ymin, padding=0):
return contours[maxIndex]
img = closeHolesOfSubImage(img)
contour = getContour(img, flag=cv2.CHAIN_APPROX_SIMPLE)
if len(contour) < 5:
contour = getContour(img, flag=cv2.CHAIN_APPROX_NONE)
contour = getContour(img, contourMode=cv2.CHAIN_APPROX_NONE)
for i in range(len(contour)):
contour [i][0][0] += xmin-padding
......
......@@ -20,6 +20,26 @@ along with this program, see COPYING.
If not, see <https://www.gnu.org/licenses/>.
"""
class ColorClassifier(object):
def __init__(self):
hue_tolerance = 50
self.colors = [ColorRangeHSV('yellow', 30, hue_tolerance, 40, 255),
ColorRangeHSV('blue', 120, hue_tolerance, 40, 255),
ColorRangeHSV('red', 180, hue_tolerance, 40, 255),
ColorRangeHSV('red', 0, hue_tolerance, 40, 255),
ColorRangeHSV('green', 70, hue_tolerance, 40, 255),
ColorRangeHSV('white', 128, 256, 0, 40)]
def classifyColor(self, meanHSV):
result = 'non-determinable'
for color in self.colors:
if color.containsHSV(meanHSV):
result = color.name
break
return result
class ColorRangeHSV(object):
def __init__(self, name, hue, hue_tolerance, min_sat, max_sat):
self.name = name
......@@ -40,22 +60,4 @@ class ColorRangeHSV(object):
else:
if sat < 128 and hsv[2] > 70:
return True
class ColorClassifier(object):
def __init__(self):
hue_tolerance = 50
self.colors = [ColorRangeHSV('yellow', 30, hue_tolerance, 40, 255),
ColorRangeHSV('blue', 120, hue_tolerance, 40, 255),
ColorRangeHSV('red', 180, hue_tolerance, 40, 255),
ColorRangeHSV('red', 0, hue_tolerance, 40, 255),
ColorRangeHSV('green', 70, hue_tolerance, 40, 255),
ColorRangeHSV('white', 128, 256, 0, 40)]
def classifyColor(self, meanHSV):
result = 'non-determinable'
for color in self.colors:
if color.containsHSV(meanHSV):
result = color.name
break
return result
\ No newline at end of file
\ No newline at end of file
......@@ -22,6 +22,28 @@ If not, see <https://www.gnu.org/licenses/>.
import cv2
from errors import InvalidParticleError
class ShapeClassifier(object):
def __init__(self):
self.shapeClasses = [Spherule(), Fibre(), Irregular()]
def classifyShape(self, contour, particleHeight):
newShape = BaseShape()
newShape.contour = contour
newShape.height = particleHeight
newShape.getParticleCharacteristics()
mostFittingCriteria = 0
bestFittingShape = 'unknown'
for testShape in self.shapeClasses:
numFits = newShape.fitsOtherShapeCriteria(testShape)
if numFits > mostFittingCriteria:
mostFittingCriteria = numFits
bestFittingShape = testShape.name
return bestFittingShape
class BaseShape(object):
def __init__(self):
self.name = None
......@@ -124,26 +146,4 @@ class Fibre(BaseShape):
self.name = 'fibre'
self.aspectRatioRange = [3, 1000]
self.solidityRange = [0.0, 0.4]
self.height2AverageLengthRange = [0, 1000]
class ShapeClassifier(object):
def __init__(self):
self.shapeClasses = [Spherule(), Fibre(), Irregular()]
def classifyShape(self, contour, particleHeight):
newShape = BaseShape()
newShape.contour = contour
newShape.height = particleHeight
newShape.getParticleCharacteristics()
mostFittingCriteria = 0
bestFittingShape = 'unknown'
for testShape in self.shapeClasses:
numFits = newShape.fitsOtherShapeCriteria(testShape)
if numFits > mostFittingCriteria:
mostFittingCriteria = numFits
bestFittingShape = testShape.name
return bestFittingShape
\ No newline at end of file
self.height2AverageLengthRange = [0, 1000]
\ No newline at end of file
......@@ -101,7 +101,7 @@ class ParticleContainer(object):
for index, particle in enumerate(self.particles):
particle.__dict__.update(particlestats[index].__dict__)
def testForInconsistentParticles(self): #i.e., particles that have multiple measurements with different assignments
def testForInconsistentParticleAssignments(self): #i.e., particles that have multiple measurements with different assignments
self.inconsistentParticles = []
for particle in self.particles:
if not particle.measurementsHaveSameOrigAssignment():
......
......@@ -100,7 +100,7 @@ class ParticleContextMenu(QtWidgets.QMenu):
self.colorMenu = QtWidgets.QMenu("Set Particle Color To")
self.colorActs = []
for color in ['white', 'black', 'blue', 'brown', 'green', 'grey', 'non-determinable', 'red', 'transparent', 'yellow']:
for color in ['white', 'black', 'blue', 'brown', 'green', 'grey', 'non-determinable', 'red', 'transparent', 'yellow', 'violet']:
self.colorActs.append(self.colorMenu.addAction(color))
self.shapeMenu = QtWidgets.QMenu("Set Particle Shape To")
......
......@@ -99,9 +99,22 @@ class ParticlePainter(QtWidgets.QGraphicsItem):
self.editorParent.acceptPaintedResult()
def drawParticle(self, pixelPos):
self.adjustMarginsAndPlaceImage(pixelPos)
center = (int(pixelPos.x()), int(pixelPos.y()))
if self.painting:
cv2.circle(self.img, center, self.radius, 255, -1)
elif self.erasing:
cv2.circle(self.img, center, self.radius, 0, -1)
self.setPixmap()
self.update()
def adjustMarginsAndPlaceImage(self, pixelPos):
x_min, x_max = round(pixelPos.x()-self.radius), round(pixelPos.x()+self.radius)
y_min, y_max = round(pixelPos.y()-self.radius), round(pixelPos.y()+self.radius)
#TODO: Using cv2.warpAffine would probably be more elegant....
x_shift = y_shift = int(0)
if x_min < 0:
......@@ -146,17 +159,7 @@ class ParticlePainter(QtWidgets.QGraphicsItem):
self.img = np.uint8(newImg)
self.setBrect()
center = (int(pixelPos.x()), int(pixelPos.y()))
if self.painting:
cv2.circle(self.img, center, self.radius, 255, -1)
elif self.erasing:
cv2.circle(self.img, center, self.radius, 0, -1)
self.setPixmap()
self.update()
def paint(self, painter, option, widget):
painter.setPen(QtCore.Qt.white)
painter.drawRect(self.brect)
......
......@@ -437,6 +437,7 @@ class SQLExport(QtWidgets.QDialog):
self.dbAssignments.updateAssignment(polymerType, result, categ_result, paint_remark)
self.dbAssignments.save()
class LinkedLineEdit(QtWidgets.QLineEdit):
def __init__(self, combobox):
......
......@@ -133,7 +133,6 @@ class DataSet(object):
'blurRadius': 9,
'threshold': 0.2,
'maxholebrightness': 0.5,
'erodeconvexdefects': 0,
'minparticlearea': 20,
'minparticledistance': 20,
'measurefrac': 1,
......
......@@ -20,7 +20,7 @@ If not, see <https://www.gnu.org/licenses/>.
"""
import numpy as np
from PyQt5 import QtCore, QtWidgets, QtGui
from segmentation import Segmentation, MeasurementPoint
from segmentation import Segmentation
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
import matplotlib.pyplot as plt
from threading import Thread
......@@ -164,7 +164,7 @@ class ImageView(QtWidgets.QLabel):
self.seeddeletepoints = arr[ind,:].tolist()
def mousePressEvent(self, event):
if event.button()==QtCore.Qt.LeftButton and event.modifiers() & QtCore.Qt.ControlModifier:
if event.button()==QtCore.Qt.LeftButton:
if event.modifiers()&QtCore.Qt.ShiftModifier:
self.drag = "delete"
elif event.modifiers()&QtCore.Qt.AltModifier:
......@@ -234,7 +234,6 @@ class ImageView(QtWidgets.QLabel):
self.overlay = pix
def paintEvent(self, event):
painter = QtGui.QPainter(self)
painter.drawPixmap(0, 0, self.pixmap())
painter.setOpacity(self.alpha)
......@@ -249,7 +248,6 @@ class ImageView(QtWidgets.QLabel):
painter.setBrush(QtCore.Qt.red)
for p in self.measpoints:
for point in self.measpoints[p]:
# painter.drawEllipse(p[0]-2, p[1]-2, 5, 5)
painter.drawEllipse(point.x-2, point.y-2, 5, 5)
if self.showseedpoints:
......@@ -666,12 +664,10 @@ class ParticleDetectionView(QtWidgets.QWidget):
self.dataset.ramanscandone = False
particleContainer = self.dataset.particleContainer
numParticles = len(contours)
particleContainer.initializeParticles(numParticles)
particleContainer.setParticleContours(contours)
particleContainer.setParticleStats(particlestats)
# particleContainer.applyPixelScaleToParticleStats(self.dataset.getPixelScale())
for particleIndex in measurementPoints.keys():
measPoints = measurementPoints[particleIndex]
......@@ -684,26 +680,3 @@ class ParticleDetectionView(QtWidgets.QWidget):
self.dataset.particleDetectionDone = True
self.dataset.mode = "prepareraman"
self.dataset.save()
if __name__ == "__main__":
import cv2
import sys
from helperfunctions import cv2imread_fix
from dataset import DataSet
from time import time
fname = r"C:\Users\brandt\Desktop\20180723DemoTZW\fullimage.tif"
app = QtWidgets.QApplication(sys.argv)
t2 = time()
img = cv2imread_fix(fname)
t3 = time()
print("OpenCV read:", t3-t2)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
ds = DataSet("dummy")
view = ParticleDetectionView(img, ds, None)
view.setDataSet(ds)
view.show()
app.exec_()
\ No newline at end of file
......@@ -47,7 +47,7 @@ def imageStacking(colimgs):
im = np.uint8(im)
return im, zval
def combineImages(path, nx, ny, nk, width, height, angle):
def combineImages(path, nx, ny, nk, width, height, angle): #TODO: Currently not used anywhere. Remove? Might be useful, still...
full = None
for i in range(nx):
for j in range(ny):
......@@ -70,13 +70,4 @@ def combineImages(path, nx, ny, nk, width, height, angle):
else:
full = cv2.max(full,dst)
cv2.imwrite("full_dunkel.png", full)
if __name__ == "__main__":
path = "../Bildserie-Scan/dunkelfeld/"
Nx, Ny, Nk = 10, 10, 4
width, height, angle = 463.78607177734375, 296.0336608886719, -0.04330849274992943
combineImages(path, Nx, Ny, Nk, width, height, angle)
\ No newline at end of file
cv2.imwrite("full_dunkel.png", full)
\ No newline at end of file
......@@ -112,12 +112,10 @@ def legacyConversion(dset, recreatefullimage=False):
def transferParticleStatsToParticleContainer(dset):
dset.particleContainer = ParticleContainer(dset)
dset.particleContainer.initializeParticles(len(dset.particlestats))
dset.particleContainer.setParticleContours(dset.particlecontours)
# dset.particleContainer.setParticleStats(dset.particlestats)
assert len(dset.particleContainer.particles) == len(dset.particlestats)
#particlestats is list of [long, short, longellipse, shortellipse, cv2.contourArea(cnt)]
for index, particle in enumerate(dset.particleContainer.particles):
particle.longSize_box = float(dset.particlestats[index][0])
particle.shortSize_box = float(dset.particlestats[index][1])
......@@ -152,7 +150,7 @@ def transferParticleStatsToParticleContainer(dset):
meas.setAssignment(dset.results['polymers'][specIndex])
meas.setHQI(dset.results['hqis'][specIndex])
dset.particleContainer.testForInconsistentParticles()
dset.particleContainer.testForInconsistentParticleAssignments()
def updateParticleStats(dset):
def markForDeletion(particle):
......
......@@ -472,13 +472,11 @@ class SampleView(QtWidgets.QGraphicsView):
data = cv2.cvtColor(cv2imread_fix(fname), cv2.COLOR_BGR2RGB)
self.imgdata = data
if data is not None:
height, width, channel = data.shape
bytesPerLine = 3 * width
pix = QtGui.QPixmap()
pix.convertFromImage(QtGui.QImage(data.data,
width, height, bytesPerLine, QtGui.QImage.Format_RGB888))
self.item.setPixmap(pix)
if self.darkenPixmap:
......
......@@ -88,14 +88,4 @@ class ScaleBar(QtWidgets.QMdiSubWindow):
qp.drawRect((WX-self.wscale)//2,y0,self.wscale,dy)
qp.drawText((WX-self.wscale)//2,5,self.wscale,dy+20,
QtCore.Qt.AlignCenter, str(int(self.divisor))+" µm")
qp.end()
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
meas = ScaleBar(None)
meas.show()
sys.exit(app.exec_())
\ No newline at end of file
qp.end()
\ No newline at end of file
......@@ -225,26 +225,6 @@ class Segmentation(object):
return edges
def erodeConvexDefects(self, thresh, numiter):
thresh = cv2.copyMakeBorder(thresh, 1, 1, 1, 1, 0)
for iterations in range(numiter):
if cv2.__version__ > '3.5':
contours, hierarchy = cv2.findContours(thresh.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
else:
thresh2, contours, hierarchy = cv2.findContours(thresh.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
for cnt in contours:
hull = cv2.convexHull(cnt, returnPoints = False)
defects = cv2.convexityDefects(cnt, hull)
if defects is not None:
sqarea = np.sqrt(cv2.contourArea(cnt))
blobsize = int(sqarea/15 * 1/(iterations+1))
for i in range(defects.shape[0]):
s, e, f, d = defects[i,0]
if d > sqarea*5:
cv2.circle(thresh,tuple(cnt[f][0]),blobsize,0,-1)
return thresh[1:-1, 1:-1]
def filterThresholdByAreas(self, thresh, minarea, maxarea):
newthresh = np.zeros_like(thresh)
n, labels, stats, centroids = cv2.connectedComponentsWithStats(thresh, 8, cv2.CV_32S)
......
......@@ -79,6 +79,9 @@ class SegmentationContour(QtWidgets.QGraphicsItem):
painter.setBrush(self.color)
painter.drawPolygon(self.polygon)
self.viewparent.update()
else:
print('painting not present contour')
def contextMenuEvent(self, event):
if self.isSelected:
......@@ -274,10 +277,10 @@ class ParticleInfo(QtWidgets.QGraphicsItem):
self.entries['Assignment:'] = self.particle.getParticleAssignment()
self.entries['Long Size (µm):'] = int(round(self.particle.longSize))
self.entries['Short Size (µm):'] = int(round(self.particle.shortSize))
self.entries['Average Height (µm)'] = int(round(self.particle.height))
self.entries['Average Height (µm):'] = int(round(self.particle.height))
self.entries['Color:'] = self.particle.color
self.entries['Shape:'] = self.particle.shape
self.entries['Numbers of Spectra'] = len(self.particle.getMeasurementIndices())
self.entries['Numbers of Spectra:'] = len(self.particle.getMeasurementIndices())
def setToAdequatePosition(self):
x = self.partCenter[0] - 200
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment