Skip to content

Commit

Permalink
Improve point-cloud file format handling (#262)
Browse files Browse the repository at this point in the history
- Add `openpyxl` to recipe files 
- Allow csv and xlxs formats in point-cloud file & add error dialog
  • Loading branch information
DanicaSTFC authored Jun 4, 2024
1 parent 2170d09 commit 1b85731
Show file tree
Hide file tree
Showing 5 changed files with 36 additions and 10 deletions.
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# ChangeLog

## vx.x.x
* Add `openpyxl` to recipe files #262
* Allow csv and xlxs formats in point-cloud file & add error dialog #262
* Set registration-box-size default and help text #259
* Make dimensionality 3D the default #256
* Edit README.md to include Prof. Bay citations and ref to DVC executable #255
Expand Down
3 changes: 2 additions & 1 deletion recipe/dev_environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ channels:
- ccpi
- paskino
- conda-forge
dependencies:
dependencies:
- openpyxl
- python
- numpy
- scipy
Expand Down
2 changes: 2 additions & 0 deletions recipe/meta.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@ requirements:
- numpy
- typing_extensions # [py<=37]
run:
- openpyxl
- python
- numpy
- scipy
- ccpi-viewer >=22.4.0
- ccpi-dvc >=22.0.0
- natsort
Expand Down
34 changes: 29 additions & 5 deletions src/idvc/dvc_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
# Author: Edoardo Pasca (UKRI-STFC)

import os
from openpyxl import load_workbook
import sys
import PySide2
from PySide2 import QtCore, QtGui, QtWidgets
Expand Down Expand Up @@ -99,6 +100,7 @@

from idvc.utils.AutomaticRegistration import AutomaticRegistration

allowed_point_cloud_file_formats = ('.txt','.csv','.xlsx')
class MainWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
Expand Down Expand Up @@ -2895,7 +2897,8 @@ def CreatePointCloudPanel(self):
2 300 750.2 209\n\
etc.\n\
Non-integer voxel locations are admitted, with reference volume interpolation used as needed.\n\
The first point is significant, as it is used as a global starting point and reference for the rigid_trans variable.")
The first point is significant, as it is used as a global starting point and reference for the rigid_trans variable.\n\
File format allowed: 'txt', 'csv, 'xlxs'.")
pc['roi_browse'].clicked.connect(self.select_pointcloud)
self.graphWidgetFL.setWidget(widgetno, QFormLayout.FieldRole, pc['roi_browse'])
widgetno += 1
Expand Down Expand Up @@ -3072,6 +3075,9 @@ def updatePointCloudPanel(self):
self.overlapXValueEntry.setEnabled(False)

def select_pointcloud(self): #, label):
"""Opens a dialog to select the pointcloud from a file.
Runs the creation or loading of the point cloud in a worker.
Sets the attribut `self.pointcloud_is` to 'loaded'."""
dialogue = QFileDialog()
self.roi = None
self.roi = dialogue.getOpenFileName(self,"Select a roi")[0]
Expand All @@ -3089,6 +3095,8 @@ def select_pointcloud(self): #, label):
self.pointcloud_is = 'loaded'

def PointCloudWorker(self, type, filename = None, disp_file = None, vector_dim = None):
"""Runs the worker to create or load the point cloud.
If the format of the point-cloud file is not in the allowed list it displays an error dialog."""
if type == "create":
#if not self.pointCloudCreated:
self.clearPointCloud()
Expand All @@ -3100,8 +3108,15 @@ def PointCloudWorker(self, type, filename = None, disp_file = None, vector_dim =
self.pointcloud_worker.signals.result.connect(self.DisplayLoadedPointCloud)
elif type == "load pointcloud file":
self.clearPointCloud()
self.pointcloud_worker = Worker(self.loadPointCloud, self.roi)
self.pointcloud_worker.signals.result.connect(self.DisplayLoadedPointCloud)
if self.roi.endswith(allowed_point_cloud_file_formats):
self.pointcloud_worker = Worker(self.loadPointCloud, self.roi)
self.pointcloud_worker.signals.result.connect(self.DisplayLoadedPointCloud)
else:
error_title = "FILE FORMAT ERROR"
error_text = f"Error reading the point-cloud file {self.roi}. Allowed formats are {allowed_point_cloud_file_formats}."
self.displayFileErrorDialog(message=error_text, title=error_title)
return

elif type == "create without loading":
#if not self.pointCloudCreated:
self.clearPointCloud()
Expand Down Expand Up @@ -3414,15 +3429,24 @@ def createPointCloud(self, **kwargs):
return True

def loadPointCloud(self, *args, **kwargs):
"""Loads a pointcloud from file.
File formats allowed are 'txt', 'csv', 'xlxs'.
"""
time.sleep(0.1) #required so that progress window displays
pointcloud_file = os.path.abspath(args[0])
progress_callback = kwargs.get('progress_callback', None)
progress_callback.emit(20)
#self.clearPointCloud() #need to clear current pointcloud before we load next one TODO: move outside thread
progress_callback.emit(30)
self.roi = pointcloud_file

points = np.loadtxt(self.roi)
if pointcloud_file.endswith('.txt'):
points = np.loadtxt(pointcloud_file)
elif pointcloud_file.endswith('.csv'):
points = np.genfromtxt(pointcloud_file, delimiter=',')
elif pointcloud_file.endswith('.xlsx'):
workbook = load_workbook(pointcloud_file, read_only=True)
sheet = workbook.active
points = np.array(list(sheet.values))
# except ValueError as ve:
# print(ve)
# return
Expand Down
5 changes: 1 addition & 4 deletions src/idvc/pointcloud_conversion.py
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,7 @@ def __init__(self):
def GetPoints(self):
'''Returns the Points'''
return self._Points

def SetData(self, value):
'''Sets the points from a numpy array or list'''
if not isinstance (value, numpy.ndarray) :
Expand All @@ -441,12 +442,10 @@ def SetData(self, value):
def GetData(self):
return self._Data


def GetNumberOfPoints(self):
'''returns the number of points in the point cloud'''
return self._Points.GetNumberOfPoints()


def FillInputPortInformation(self, port, info):
# if port == 0:
# info.Set(vtk.vtkAlgorithm.INPUT_REQUIRED_DATA_TYPE(), "vtkImageData")
Expand All @@ -457,7 +456,6 @@ def FillOutputPortInformation(self, port, info):
return 1

def RequestData(self, request, inInfo, outInfo):

# print ("Request Data")
# output_image = vtk.vtkDataSet.GetData(inInfo[0])
pointPolyData = vtk.vtkPolyData.GetData(outInfo)
Expand All @@ -472,7 +470,6 @@ def RequestData(self, request, inInfo, outInfo):
pointPolyData.SetVerts(self._Vertices)
return 1


def FillCells(self):
'''Fills the Vertices'''
vertices = self._Vertices
Expand Down

0 comments on commit 1b85731

Please sign in to comment.