diff --git a/AmpScan/AmpScanGUI.py b/AmpScan/AmpScanGUI.py index 38b6f52a307d2e8c9064a4b26c4175d1b5eac129..80dd68e87ac164e5e78b7d8eb5714134ef80cace 100644 --- a/AmpScan/AmpScanGUI.py +++ b/AmpScan/AmpScanGUI.py @@ -18,6 +18,7 @@ class AmpScanGUI(QMainWindow): def __init__(self, parent = None): super(AmpScanGUI, self).__init__() self.vtkWidget = qtVtkWindow() + self.renWin = self.vtkWidget._RenderWindow self.mainWidget = QWidget() self.AmpObj = None # self.CMap = np.array([[212.0, 221.0, 225.0], @@ -36,13 +37,13 @@ class AmpScanGUI(QMainWindow): self.fname = QFileDialog.getOpenFileName(self, 'Open file', filter="Meshes (*.stl)") if self.AmpObj is not None: - self.vtkWidget.renderActors(self.AmpObj.actors, []) + self.renWin.renderActors(self.AmpObj.actors, []) self.AmpObj = AmpObject(self.fname[0], 'limb') self.AmpObj.addActor(stype='limb') self.AmpObj.lp_smooth(stype='limb') - self.vtkWidget.setnumViewports(1) - self.vtkWidget.setProjection() - self.vtkWidget.renderActors(self.AmpObj.actors, ['limb',]) + self.renWin.setnumViewports(1) + self.renWin.setProjection() + self.renWin.renderActors(self.AmpObj.actors, ['limb',]) def chooseSocket(self): self.sockfname = QFileDialog.getOpenFileName(self, 'Open file', @@ -52,17 +53,17 @@ class AmpScanGUI(QMainWindow): self.AmpObj.lp_smooth(stype='socket') def align(self): - self.vtkWidget.setnumViewports(2) - self.vtkWidget.setView(view=[-1, 0, 0], viewport=1) - self.vtkWidget.setProjection(True, 0) - self.vtkWidget.setProjection(True, 1) -# self.vtkWidget.render(self.AmpObj.actors, dispActors=['limb',]) -# self.vtkWidget.render(self.AmpObj.actors, dispActors=['socket',], + self.renWin.setnumViewports(2) + self.renWin.setView(view=[-1, 0, 0], viewport=1) + self.renWin.setProjection(True, 0) + self.renWin.setProjection(True, 1) +# self.renWin.render(self.AmpObj.actors, dispActors=['limb',]) +# self.renWin.render(self.AmpObj.actors, dispActors=['socket',], # viewport=1) - self.vtkWidget.renderActors(self.AmpObj.actors, + self.renWin.renderActors(self.AmpObj.actors, dispActors=['limb', 'socket'], viewport=0) - self.vtkWidget.renderActors(self.AmpObj.actors, + self.renWin.renderActors(self.AmpObj.actors, dispActors=['limb', 'socket'], viewport=1) self.AmpObj.actors['limb'].setColor([1.0, 0.0, 0.0]) @@ -71,14 +72,14 @@ class AmpScanGUI(QMainWindow): self.AmpObj.actors['socket'].setOpacity(0.5) def register(self): - self.vtkWidget.setnumViewports(1) - self.vtkWidget.setProjection() + self.renWin.setnumViewports(1) + self.renWin.setProjection() self.RegObj = regObject(self.AmpObj) self.RegObj.registration(steps=5, baseline='socket', target='limb', reg = 'reglimb', direct=True) self.RegObj.addActor(stype='reglimb', CMap=self.AmpObj.CMapN2P) - self.vtkWidget.renderActors(self.AmpObj.actors, ['reglimb',], shading=False) - self.vtkWidget.setScalarBar(self.AmpObj.actors['reglimb']) + self.renWin.renderActors(self.AmpObj.actors, ['reglimb',], shading=False) + self.renWin.setScalarBar(self.AmpObj.actors['reglimb']) def analyse(self): self.RegObj.plot_slices() @@ -86,20 +87,20 @@ class AmpScanGUI(QMainWindow): def chooseFE(self): FEname = QFileDialog.getOpenFileName(self, 'Open file', filter="FE results (*.npy)") - self.vtkWidget.setnumViewports(1) + self.renWin.setnumViewports(1) self.AmpObj.addFE([FEname[0],]) self.AmpObj.lp_smooth('FE', n=1) self.AmpObj.addActor(stype='FE', CMap=self.AmpObj.CMap02P, bands=5) self.AmpObj.actors['FE'].setScalarRange(smin=0.0, smax=50) - self.vtkWidget.renderActors(self.AmpObj.actors, ['FE',], shading=True) - self.vtkWidget.setScalarBar(self.AmpObj.actors['FE']) + self.renWin.renderActors(self.AmpObj.actors, ['FE',], shading=True) + self.renWin.setScalarBar(self.AmpObj.actors['FE']) def choosePress(self): vName = QFileDialog.getOpenFileName(self, 'Open file', filter="Sensor vertices (*.csv)") pName = QFileDialog.getOpenFileName(self, 'Open file', filter="Sensor pressures (*.csv)") - self.vtkWidget.setnumViewports(1) + self.renWin.setnumViewports(1) self.pSense = pressSense() self.pSense.calcFaces(d=5) self.pSense.importVert(vName[0]) @@ -108,8 +109,8 @@ class AmpScanGUI(QMainWindow): self.AmpObj.actors['antS'] = self.pSense.actors['antS'] self.AmpObj.actors['socket'].setColor([1.0, 1.0, 1.0]) self.AmpObj.actors['socket'].setOpacity(1.0) - self.vtkWidget.renderActors(self.AmpObj.actors, ['socket', 'antS']) - self.vtkWidget.setScalarBar(self.AmpObj.actors['antS']) + self.renWin.renderActors(self.AmpObj.actors, ['socket', 'antS']) + self.renWin.setScalarBar(self.AmpObj.actors['antS']) def createActions(self): self.openFile = QAction(QIcon('open.png'), 'Open', self, diff --git a/AmpScan/ampVis.py b/AmpScan/ampVis.py index 57d38daf1acc454983058196cfedf6ae4a097784..165d848f15ae476e765e093efd10923bd4352024 100644 --- a/AmpScan/ampVis.py +++ b/AmpScan/ampVis.py @@ -5,13 +5,14 @@ Created on Thu Sep 28 13:19:18 2017 @author: js22g12 Functions that deal with the visualisation of the limb and data + +Includes interfaces to deal """ import numpy as np import vtk from vtk.util import numpy_support from vtk.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor -from PyQt5.QtWidgets import QWidget class vtkRender(vtk.vtkRenderer): """ @@ -69,18 +70,23 @@ class ampVTK(object): self.scalar_bar.SetPosition2(0.1, 0.3) self.rens[0].AddActor(self.scalar_bar) - - def setView(self, view = [0, -1, 0], viewport=0): self.cams[viewport].SetPosition(view[0], view[1], view[2]) self.cams[viewport].SetViewUp(-0.0, 0.0, 1.0) def setBackground(self, color=[0.1, 0.2, 0.4]): + """ + Set the background colour of the renderer + """ for ren in self.rens: ren.SetBackground(color) def setProjection(self, perspective=False, viewport=0): - self.cams[viewport].SetParallelProjection(perspective) + """ + Set the projection of the camera to either parallel or perspective + + """ + self.cams[viewport].SetParallelProjection(perspective) def addAxes(self, actors, viewport=0, color = [1.0, 1.0, 1.0], font=None): @@ -109,55 +115,6 @@ class ampVTK(object): # self.rens[viewport].AddActor(self.axes[viewport]) -class qtVtkWindow(QVTKRenderWindowInteractor, ampVTK): - - def __init__(self): - super(qtVtkWindow, self).__init__() - self.SetInteractorStyle(self.style) - self.GetRenderWindow().AddRenderer(self.rens[0]) - self.iren = self.GetRenderWindow().GetInteractor() - self.iren.Initialize() - - def setnumViewports(self, n): - """ - Function to set multiple viewports within the vtkWindow - - Parameters - ------------ - n: int - number of viewports required - """ - dif = n - len(self.rens) - if dif == 0: - return - elif dif < 0: - for ren in self.rens[n:]: - self.GetRenderWindow().RemoveRenderer(ren) - self.rens = self.rens[:n] - elif dif > 0: - for i in range(dif): - self.rens.append(vtkRender()) - self.axes.append(vtk.vtkCubeAxesActor()) - self.GetRenderWindow().AddRenderer(self.rens[-1]) - if len(self.cams) < len(self.rens): - self.cams.append(vtk.vtkCamera()) - self.rens[-1].SetActiveCamera(self.cams[len(self.rens)-1]) - for i, ren in enumerate(self.rens): - ren.SetViewport(float(i)/n, 0, float(i+1)/n, 1) - self.setBackground() - - -class vtkRenWin(vtk.vtkRenderWindow, ampVTK): - - def __init__(self, winWidth=512, winHeight=512): - super(vtkRenWin, self).__init__() - self.winWidth = winWidth - self.winHeight = winHeight - self.SetSize(self.winWidth, self.winHeight) - self.OffScreenRenderingOn() - self.AddRenderer(self.rens[0]) - self.Render() - def setnumViewports(self, n): """ Function to set multiple viewports within the vtkWindow @@ -188,11 +145,11 @@ class vtkRenWin(vtk.vtkRenderWindow, ampVTK): def getImage(self): - self.vtkRGB = vtk.vtkUnsignedCharArray() + vtkRGB = vtk.vtkUnsignedCharArray() self.GetPixelData(0, 0, self.winWidth-1, self.winHeight-1, - 1, self.vtkRGB) - self.vtkRGB.Squeeze() - self.im = np.flipud(np.resize(np.array(self.vtkRGB), + 1, vtkRGB) + vtkRGB.Squeeze() + self.im = np.flipud(np.resize(np.array(vtkRGB), [self.winWidth, self.winHeight, 3])) / 255.0 def getScreenshot(self, fname, mag=10): @@ -209,17 +166,54 @@ class vtkRenWin(vtk.vtkRenderWindow, ampVTK): writer.Write() +class vtkRenWin(vtk.vtkRenderWindow, ampVTK): + + def __init__(self, qt = True, winWidth=512, winHeight=512): + super(vtkRenWin, self).__init__() + self.AddRenderer(self.rens[0]) + if qt is False: + self.winWidth = winWidth + self.winHeight = winHeight + self.SetSize(self.winWidth, self.winHeight) + self.OffScreenRenderingOn() + self.Render() + + +class qtVtkWindow(QVTKRenderWindowInteractor): + """ + Create a vtk window to be embeded within a qt GUI + Inherites the QVTKRenderWindowInteractor class and the + + Fix issue with SetInteractorStyle + """ + + def __init__(self): + super(qtVtkWindow, self).__init__(rw=vtkRenWin()) + #self.SetInteractorStyle(self.style) + self.iren = self._RenderWindow.GetInteractor() + self.iren.Initialize() + class visMixin(object): + """ + Visualisation methods that act upon the AmpObj itself + Methods for generating the custom AmpObj actor to interface with vtk + """ def genIm(self, actor=['limb'], winWidth=512, winHeight=512, views=[[0, -1, 0]], background=[1.0, 1.0, 1.0], projection=True, shading=True, mag=10, out='im', name='test.tiff'): """ + Output an image of an actor either as an array or a saved png file + """ - win = vtkRenWin(winWidth, winHeight) + # Generate a renderer window + win = vtkRenWin(False, winWidth, winHeight) + # Set the number of viewports win.setnumViewports(len(views)) + # Set the background colour win.setBackground(background) + # Set camera projection win.setProjection(projection) for i, view in enumerate(views): win.addAxes(self.actors, color=[0.0, 0.0, 0.0], viewport=i) @@ -227,8 +221,8 @@ class visMixin(object): win.setProjection(projection, viewport=i) win.renderActors(self.actors, actor, viewport=i, shading=shading, zoom=1.3) win.Render() - win.getImage() if out == 'im': + win.getImage() return win.im elif out == 'fh': win.getScreenshot(name) @@ -252,8 +246,6 @@ class visMixin(object): Class that inherits methods from vtk actor Contains functions to set vertices, faces, scalars and color map from numpy arrays - - Add functions to add vert, add faces, cmap and make LUT """ def __init__(self, data, CMap=None, bands=128): diff --git a/AmpScan/core.py b/AmpScan/core.py index 027a86dec54733a2dadcc52f45560f772baf777c..37c95ac3dba5f2b4536e5fcdd4666fae9f6f2ed8 100644 --- a/AmpScan/core.py +++ b/AmpScan/core.py @@ -6,8 +6,6 @@ Created on Wed Sep 13 13:54:23 2017 Core functions for the AmpObject -Remove pd dependency and instead just use numpy arrays - Requires numpy 1.13 @@ -69,6 +67,14 @@ class AmpObject(alignMixin, trimMixin, smoothMixin, analyseMixin, else: raise ValueError('dtype not supported, please choose from ' + 'limb, socket, reglimb, regsocket, MRI or AmpObj') + + def createCMap(self, cmap=None, n = 50): + """ + Function to generate a colormap for the AmpObj + """ + if cmap is None: + cmap = n + def addData(self, Data, stype): @@ -177,6 +183,9 @@ class AmpObject(alignMixin, trimMixin, smoothMixin, analyseMixin, data['faceEdges'][eF[~logic], 1] = fInd[~logic] def vNorm(self, stype=0): + """ + Function to compute the vertex normals + """ if isinstance(stype, int): stype = self.stype[stype] data = getattr(self, stype) diff --git a/README.rst b/README.rst deleted file mode 100644 index a4d653ee843ab92011c009c15d5dcdfb3f3078e4..0000000000000000000000000000000000000000 --- a/README.rst +++ /dev/null @@ -1,104 +0,0 @@ -AmpScan ReadME File --------------------- - -Author: Joshua Steer - -To install the package, open the cmd prompt and type the following script - -J: -cd J:\Shared Resources\AmpScan IfLS Team\100 PYTHON\Code -pip install . - -To use the package - - import AmpScan - import os - - read_filename = '01_PhantomShell_ICEM_3mm.stl' - write_filename = '01_PhantomShell_ICEM_3mm_write.stl' - - Data = AmpObject(target, 'limb') - Data.addData(baseline, 'socket') - Data.lp_smooth() - Data.man_rot([5,5,5]) - Reg = regObject(Data) - Reg.registration(steps=5, baseline='socket', target='limb', reg = 'reglimb') - Reg.save(saveStr, stype='reglimb') - Reg.plot_slices() - - - -Structure: - -AmpScan structure - -core.py - class AmpObject(Data, stype) - func addData - func read_stl - func unify_vertices - func computeEdges - func save - func calc_norm - func man_trans - func man_rot - func centre -smooth.py - class smoothMixin - func lp_smooth -autoAlign.py - class alignMixin - func icp - func calcDistError -trim.py - class trimMixin - func planarTrim -analyse.py - class analyseMixin - func plot_slices - func create_slice - func planeEdgeIntersect -ampVis.py - class visMixin - func genIm - func addActor - class ampActor - func setVert - func setFaces - func setRect - func setColor - func setOpacity - func setCMap - class vtkRender - class vtkWindow - func render - func setScalarBar - func setViewports - func addAxes -registration.py - class regObject - func registration -TSBSocketDesign.py - class mplCanvas - class dragSpline - func connect - func on_press - func on_motion - func on_release - func disconnect - func bezierCurve -SocketDesignGUI.py - class GUI - func plotRect - func chooseOpenFile - func createActions - func createMenus -AmpScanGUI - class GUI - func chooseOpenFile - func chooseSocket - func align - func register - func analyse - func createActions - func createMenus