- Author:
- rjag008 <rjag008@auckland.ac.nz>
- Date:
- 2018-08-18 17:01:42+12:00
- Desc:
- Final Release
- Permanent Source URI:
- https://models.physiomeproject.org/workspace/51d/rawfile/9ea33dda840d259caf68e26d21300bece4eeff3f/digitiser/model.py
'''
Created on 15/06/2018
@author: rjag008
'''
try:
from PySide import QtCore
from PySide.QtGui import QColor, QBrush, QPen
signalHandle = QtCore.Signal
except ImportError:
#from PyQt4 import QtCore
#from PyQt4.Qt import QColor, QBrush, QPen
#signalHandle = QtCore.pyqtSignal
pass
from collections import OrderedDict
from os import path
import pickle
import numpy as np
class PaintModel(QtCore.QObject):
# Signals
modelChanged = signalHandle()
def __init__(self):
QtCore.QObject.__init__(self, None)
self.reset()
def reset(self):
self._pointIndex = 0
self._backgroundFile = None
self._points = OrderedDict()
self.datasetColors = dict()
self._datasets = OrderedDict()
self._datasetKey = 0
self._currentDataset = None
#Target axis
self._axisRefs = {}
self._mode = 0
#Current Axis
self.x0 = 0.0
self.x1 = 1.0
self.y0 = 0.0
self.y1 = 1.0
def newDataSet(self, background_file = None):
self.reset()
if not background_file is None:
self.addImage(background_file)
self.addDataset()
def saveDataset(self, projectFile):
if len(self._points)==0:
return False
# Construct dict to dump
d = {}
if not self._backgroundFile is None:
dir_name = path.dirname(str(projectFile))
rel_path = path.relpath(str(self._backgroundFile), str(dir_name))
d["background_file"] = str(rel_path)
else:
d["background_file"] = None
d["x0"] = self.x0
d["x1"] = self.x1
d["y0"] = self.y0
d["y1"] = self.y1
# Store axis refs coordinates
axis_refs = {}
for key in self._axisRefs:
if self._axisRefs[key] is None:
axis_refs[key] = None
else:
axis_refs[key] = self._axisRefs[key]
d["axisRefs"] = axis_refs
# Store items coordinates
points = []
for key in self._points:
p, dataset = self._points[key]
points.append([p, dataset])
d["points"] = points
datasets = self._datasets
datasetKey = self._datasetKey
currentDataset = self._currentDataset
datasetColors = dict()
for k,v in self.datasetColors.items():
if isinstance(v[0],QPen): #Handle difference between data stored between QPainter and ZincDigitizer
datasetColors[k] = [v[0].color().rgba()]
else:
crgba = v[1].rgba()
datasetColors[k] = [v[0],crgba]
d["datasets"] = datasets
d["datasetKey"] = datasetKey
d["currentDataset"] = currentDataset
d['datasetColors'] = datasetColors
try:
with open(projectFile, "wb") as oF:
pickle.dump("SPARCPaintV0.1", oF,2)
pickle.dump(d, oF,2)
return True
except:
return False
def reassignIds(self,d):
dsk = self._datasetKey
kmap = dict()
if "datasets" in d:
ds = d['datasets']
newDataset = OrderedDict()
for k,v in ds.items():
if k in self._datasets:
kmap[k] = dsk
newDataset[dsk] = v
dsk +=1
else:
kmap[k] = k
newDataset[k] = v
d['datasets'] = newDataset
d['datasetKey'] = dsk
if 'datasetColors' in d:
dcolors = dict()
ds = d['datasetColors']
for k,v in ds.items():
if not k in self.datasetColors:
dcolors[k] = v
d['datasetColors'] = ds
newPoints = []
for data in d["points"]:
if isinstance(data, list) or isinstance(data, tuple):
p, dataset = data
newPoints.append([p,kmap[dataset]])
else:
newPoints.append(data)
d['points'] = newPoints
return d
def loadDataset(self, projectFile):
#self.reset()
with open(projectFile, "rb") as iF:
header = pickle.load(iF)
if header == "SPARCPaintV0.1":
d = pickle.load(iF)
if len(self._datasets) > 0:
#Renumber dataset to ensure it doesnt clash with existing keys
d = self.reassignIds(d)
def assignIfExists(var, d, keyname):
if keyname in d:
return d[keyname]
else:
return var
background_file = d["background_file"]
if header == "PaintV0.3" and not background_file is None:
dir_name = path.dirname(str(projectFile))
background_file = path.normpath(path.join(dir_name, background_file))
self.addImage(background_file)
self.x0 = assignIfExists(self.x0, d, "x0")
self.x1 = assignIfExists(self.x1, d, "x1")
self.y0 = assignIfExists(self.y0, d, "y0")
self.y1 = assignIfExists(self.y1, d, "y1")
self._axisRefs = {}
for key in d["axisRefs"]:
if not d["axisRefs"][key] is None:
p = d["axisRefs"][key]
if isinstance(p, QtCore.QPointF):
p = [p.x(), p.y()]
self._axisRefs[key]=p
#self.datasetColors = dict()
if 'datasetColors' in d:
ds = d['datasetColors']
for k,v in ds.items():
if len(v)==2:
color = QColor(v[1])
self.datasetColors[k] = [v[0],color]
else:
color = QColor(v[0])
brush = QBrush()
bcolor = QColor(color)
bcolor.setAlpha(127)
brush.setColor(bcolor)
brush.setStyle(QtCore.Qt.CrossPattern)
pen = QPen(color, 1, QtCore.Qt.SolidLine,QtCore.Qt.FlatCap, QtCore.Qt.MiterJoin)
self.datasetColors[k] = [pen,brush]
if "datasets" in d:
#self._datasets = d["datasets"]
for k,v in d['datasets'].items():
self._datasets[k] = v
self._datasetKey = d["datasetKey"]
self._currentDataset = d["currentDataset"]
else:
self._datasets = OrderedDict({0: "Dataset 0"})
self._datasetKey = 1
self._currentDataset = 0
for data in d["points"]:
p, dataset = [0,0], 0
if isinstance(data, list) or isinstance(data, tuple):
p, dataset = data
elif isinstance(data, QtCore.QPointF):
p = [data.x(), data.y()]
else:
continue
if dataset in self._datasets:
self._currentDataset = dataset
self.addPointPassive(p)
self.modelChanged.emit()
return True
self.modelChanged.emit()
return False
def addImage(self, background_file):
self._backgroundFile = str(background_file)
def getBackgroundFile(self):
return self._backgroundFile
def getPoints(self):
return self._points
def addPointPassive(self, pos):
self._points[self._pointIndex] = [pos, self._currentDataset]
self._pointIndex += 1
def addPoint(self, pos):
self._points[self._pointIndex] = [pos, self._currentDataset]
self._pointIndex += 1
self.modelChanged.emit()
def removePoint(self, key):
del self._points[key]
self.modelChanged.emit()
def movePoint(self, key, pos):
if not key in self._points:
return
#Update if it belongs to current dataset
ds = self._points[key][1]
if ds == self._currentDataset:
self._points[key][0] = pos
self.modelChanged.emit()
def setInsertMode(self,mode):
self._mode = mode
def computeCoordinates(self, pos):
if len(self._axisRefs.keys()) != 4:
return pos
coord = np.zeros(2)
x0Pos = self._axisRefs[1]
x1Pos = self._axisRefs[2]
y0Pos = self._axisRefs[3]
y1Pos = self._axisRefs[4]
pos = np.array(pos)
# Find projecting over x axis
A0 = x1Pos- x0Pos
A1 = y1Pos- y0Pos
b = pos - x0Pos
alpha = np.dot(A0,b)/np.linalg.norm(A0)
b = pos - y0Pos
beta = np.dot(A1,b)/np.linalg.norm(A1)
#Go from reference axis lengths to preferred axis lengths given by (x1,y1)-(x0,y0)
scalingx = (self.x1 - self.x0)/np.linalg.norm(A0)
scalingy = (self.y1 - self.y0)/np.linalg.norm(A1)
coord[0] = self.x0 + (self.x1 - self.x0)/np.linalg.norm(self.x1-self.x0) * alpha * scalingx
coord[1] = self.y0 + (self.y1 - self.y0)/np.linalg.norm(self.y1-self.y0) * beta * scalingy
return coord
def getPointsPerDataset(self):
pointsPerDataset = OrderedDict()
for dataset in self._datasets:
pointsPerDataset[dataset] = []
for key in self._points:
p, dataset = self._points[key]
pointsPerDataset[dataset].append(p)
count = max(len(pointsPerDataset[dataset]) for dataset in self._datasets)
return pointsPerDataset, count
def getPointsForDataset(self,dataset):
pointInDataset = []
for key in self._points:
p, pdataset = self._points[key]
if pdataset==dataset:
pointInDataset.append(p)
return pointInDataset
def getDatasetInTransformedCoordinates(self):
pointsPerDataset, _ = self.getPointsPerDataset()
result = dict()
for dataset,name in self._datasets.items():
resultArray = []
for i in range(len(pointsPerDataset[dataset])):
p = pointsPerDataset[dataset][i]
resultArray.append(self.computeCoordinates(p))
result[name] = resultArray
return result
def exportToCSV(self, filename):
pointsPerDataset, count = self.getPointsPerDataset()
with open(filename, "w") as oF:
for i in range(count):
for dataset in self._datasets:
if i < len(pointsPerDataset[dataset]):
p = pointsPerDataset[dataset][i]
oF.write("%f, %f, " % tuple(self.computeCoordinates(p)))
else:
oF.write(", , ")
oF.write("\n")
def addDataset(self, name = None):
if name is None:
name = "Group %i" % self._datasetKey
key = self._datasetKey
self._datasets[key] = name
self._currentDataset = key
self._datasetKey += 1
self.modelChanged.emit()
def changeDatasetName(self,key,name):
self._datasets[key] = name
def removeDataset(self, key):
del self._datasets[key]
# Delete dangling points
for pkey in self._points.keys():
if self._points[pkey][1] == key:
del self._points[pkey]
if self._currentDataset == key:
if len(self._datasets.keys()) == 0:
# If the last dataset was removed, add a new one instead
self.addDataset()
self._currentDataset = self._datasets.keys()[-1]
if len(self._datasets.keys()) == 0:
# If the last dataset was removed, add a new one instead
self.addDataset()
self.modelChanged.emit()
def changeCurrentDataset(self, key):
self._currentDataset = key
def getDatasets(self):
return self._datasets
def getCurrentDataset(self):
return self._currentDataset
def setToScaffoldMeshScaling(self,meshCoordinateScalingFactor):
self.x0 = -0.5
self.y0 = -0.5
self.x1 = 0.5 #Mesh is bound to unit cube with centroid in the middle
self.y1 = 0.5
factor = meshCoordinateScalingFactor/2.0
self._axisRefs[1] = np.array([-factor,0.0])
self._axisRefs[2] = np.array([factor,0.0])
self._axisRefs[3] = np.array([0.0,-factor])
self._axisRefs[4] = np.array([0.0,factor])