From ce8aa5978110d5c869c765997a2166e125dbed6a Mon Sep 17 00:00:00 2001 From: Braden Date: Wed, 20 Mar 2024 11:41:51 -0600 Subject: [PATCH 01/36] Remove spatial orientation data from DisplayShape Fixed merge conflict with ProcessSofastFringe --- opencsp/app/sofast/lib/DisplayShape.py | 97 +++++++------------ opencsp/app/sofast/lib/ProcessSofastFringe.py | 17 ++-- 2 files changed, 47 insertions(+), 67 deletions(-) diff --git a/opencsp/app/sofast/lib/DisplayShape.py b/opencsp/app/sofast/lib/DisplayShape.py index 503293387..291863ff2 100644 --- a/opencsp/app/sofast/lib/DisplayShape.py +++ b/opencsp/app/sofast/lib/DisplayShape.py @@ -1,77 +1,65 @@ import numpy as np from scipy.interpolate import LinearNDInterpolator -from scipy.spatial.transform import Rotation from opencsp.common.lib.geometry.Vxy import Vxy from opencsp.common.lib.geometry.Vxyz import Vxyz import opencsp.common.lib.tool.hdf5_tools as hdf5_tools +# TODO: add HDF5 IO interface + class DisplayShape: """Representation of a screen/projector for deflectometry.""" - def __init__( - self, v_cam_screen_screen: Vxyz, r_screen_cam: Rotation, grid_data: dict, name: str = '' - ) -> 'DisplayShape': + def __init__(self, grid_data: dict, name: str = '') -> 'DisplayShape': """ Instantiates deflectometry display representation. Parameters ---------- - v_cam_screen_screen : Vxyz - Translation vector from camera to screen in screen coordinates. - r_screen_cam : Rotation - Rotation vector from camera to screen coordinates. grid_data : dict - DisplayShape distortion data. Must contain the field "screen_model" - that defines distortion model and necessary input data. + Contains different data depending on the model being used. - 1) Rectangular 2D + - Rectangular 2D - Description: Model with no distortion (useful for LCD screens, etc.). - Needs the following fields. - "screen_model" : str - 'rectangular2D' - - 2) Distorted 2D + - "screen_model" : str + - 'rectangular2D' + - "screen_x" : float + - Screen dimension in x + - "screen_y" : float + - Screen dimension in y + + - Distorted 2D - Description: Model that assumes screen ia perfectly flat 2D surface (useful for projector system with very flat wall). - Needs the following fields. - "screen_model" : str - 'distorted2D' - "xy_screen_fraction" : Vxy - XY screen points in fractional screens. - "xy_screen_coords" : Vxy - XY screen points in meters (screen coordinates). - - 3) Distorted 3D + - "screen_model" : str + - 'distorted2D' + - "xy_screen_fraction" : Vxy + - XY screen points in fractional screens. + - "xy_screen_coords" : Vxy + - XY screen points in meters (screen coordinates). + + - Distorted 3D - Description: Model that can completely define the 3D shape of a distorted screen in 3D. - Needs the following fields. - "screen_model" : str - 'distorted3D' - "xy_screen_fraction" : Vxy - XY screen points in fractional screens. - "xyz_screen_coords" : Vxyz - XYZ screen points in meters (screen coordinates). + - "screen_model" : str + - 'distorted3D' + - "xy_screen_fraction" : Vxy + - XY screen points in fractional screens. + - "xyz_screen_coords" : Vxyz + - XYZ screen points in meters (screen coordinates). name : str, optional The name of the calibrated display. - """ - - # Rotation matrices - self.r_screen_cam = r_screen_cam - self.r_cam_screen = self.r_screen_cam.inv() - - # Translation vectors - self.v_cam_screen_screen = v_cam_screen_screen - self.v_cam_screen_cam = self.v_cam_screen_screen.rotate(self.r_screen_cam) - # Save display model name self.name = name @@ -134,7 +122,6 @@ def _interp_func_rectangular2D(self, uv_display_pts: Vxy) -> Vxyz: ------- Vxyz XYZ points in display coordinates. - """ xm = (uv_display_pts.x - 0.5) * self.grid_data['screen_x'] # meters ym = (uv_display_pts.y - 0.5) * self.grid_data['screen_y'] # meters @@ -156,7 +143,6 @@ def _interp_func_2D(self, uv_display_pts: Vxy, func_xy) -> Vxyz: ------- Vxyz XYZ points in display coordinates. - """ xy = func_xy(uv_display_pts.x, uv_display_pts.y).T # (2, N) ndarray meters zm = np.zeros((1, len(uv_display_pts))) # (1, N) ndarray meters @@ -177,7 +163,6 @@ def _interp_func_3D(self, uv_display_pts: Vxy, func_xyz) -> Vxyz: ------- Vxyz XYZ points in display coordinates. - """ xyz = func_xyz(uv_display_pts.x, uv_display_pts.y).T # (3, N) ndarray meters return Vxyz(xyz) # meters, display coordinates @@ -191,41 +176,35 @@ def load_from_hdf(cls, file: str): ---------- file : string HDF5 file to load - """ # Load grid data - grid_data = hdf5_tools.load_hdf5_datasets(['DisplayShape/screen_model'], file) + datasets = ['DisplayShape/screen_model', 'DisplayShape/name'] + data = hdf5_tools.load_hdf5_datasets(datasets, file) # Rectangular - if grid_data['screen_model'] == 'rectangular2D': + if data['screen_model'] == 'rectangular2D': datasets = ['DisplayShape/screen_x', 'DisplayShape/screen_y'] - grid_data.update(hdf5_tools.load_hdf5_datasets(datasets, file)) + grid_data = hdf5_tools.load_hdf5_datasets(datasets, file) # Distorted 2D - elif grid_data['screen_model'] == 'distorted2D': + elif data['screen_model'] == 'distorted2D': datasets = ['DisplayShape/xy_screen_fraction', 'DisplayShape/xy_screen_coords'] - grid_data.update(hdf5_tools.load_hdf5_datasets(datasets, file)) + grid_data = hdf5_tools.load_hdf5_datasets(datasets, file) grid_data['xy_screen_fraction'] = Vxy(grid_data['xy_screen_fraction']) grid_data['xy_screen_coords'] = Vxy(grid_data['xy_screen_coords']) # Distorted 3D - elif grid_data['screen_model'] == 'distorted3D': + elif data['screen_model'] == 'distorted3D': datasets = ['DisplayShape/xy_screen_fraction', 'DisplayShape/xyz_screen_coords'] - grid_data.update(hdf5_tools.load_hdf5_datasets(datasets, file)) + grid_data = hdf5_tools.load_hdf5_datasets(datasets, file) grid_data['xy_screen_fraction'] = Vxy(grid_data['xy_screen_fraction']) grid_data['xyz_screen_coords'] = Vxyz(grid_data['xyz_screen_coords']) else: - raise ValueError(f'Model, {grid_data["screen_model"]}, not supported.') - - # Load display parameters - datasets = ['DisplayShape/rvec_screen_cam', 'DisplayShape/tvec_cam_screen_screen', 'DisplayShape/name'] - data = hdf5_tools.load_hdf5_datasets(datasets, file) + raise ValueError(f'Model, {data["screen_model"]}, not supported.') # Return display object kwargs = { - 'r_screen_cam': Rotation.from_rotvec(data['rvec_screen_cam']), - 'v_cam_screen_screen': Vxyz(data['tvec_cam_screen_screen']), 'name': data['name'], 'grid_data': grid_data, } @@ -252,9 +231,5 @@ def save_to_hdf(self, file: str): else: data.append(self.grid_data[dataset]) - # Screen data - datasets += ['DisplayShape/rvec_screen_cam', 'DisplayShape/tvec_cam_screen_screen', 'DisplayShape/name'] - data += [self.r_screen_cam.as_rotvec(), self.v_cam_screen_screen.data, self.name] - # Save data hdf5_tools.save_hdf5_datasets(data, datasets, file) diff --git a/opencsp/app/sofast/lib/ProcessSofastFringe.py b/opencsp/app/sofast/lib/ProcessSofastFringe.py index 271f3d2ea..6ad9eece6 100644 --- a/opencsp/app/sofast/lib/ProcessSofastFringe.py +++ b/opencsp/app/sofast/lib/ProcessSofastFringe.py @@ -11,7 +11,7 @@ from opencsp.app.sofast.lib.DefinitionFacet import DefinitionFacet from opencsp.app.sofast.lib.DisplayShape import DisplayShape as Display import opencsp.app.sofast.lib.image_processing as ip -from opencsp.app.sofast.lib.MeasurementSofastFringe import MeasurementSofastFringe as Measurement +from opencsp.app.sofast.lib.MeasurementSofastFringe import MeasurementSofastFringe from opencsp.app.sofast.lib.ParamsSofastFringe import ParamsSofastFringe import opencsp.app.sofast.lib.process_optics_geometry as po from opencsp.app.sofast.lib.SpatialOrientation import SpatialOrientation @@ -158,25 +158,30 @@ class ProcessSofastFringe(HDF5_SaveAbstract): - v_mask_centroid_image """ - def __init__(self, measurement: Measurement, camera: Camera, display: Display) -> 'ProcessSofastFringe': + def __init__(self, + measurement: MeasurementSofastFringe, + orientation: SpatialOrientation, + camera: Camera, + display: Display) -> 'ProcessSofastFringe': """ SOFAST processing class. Parameters ---------- - measurement : Measurement - Measurement class to process. + measurement : MeasurementSofastFringe + MeasurementSofastFringe class to process. + orientation : SpatialOrientation + SpatialOrientation object camera : Camera Camera object used to capture data. display : Display Display object used to capture data. - """ # Store data self.measurement = measurement self.display = display self.camera = camera - self.orientation = SpatialOrientation(display.r_cam_screen, display.v_cam_screen_cam) + self.orientation = orientation # Define default calculation parameters self.params = ParamsSofastFringe() From ed615a8257476ce1b2fa8cda2e807c3b78cb7893 Mon Sep 17 00:00:00 2001 From: Braden Date: Wed, 20 Mar 2024 13:08:22 -0600 Subject: [PATCH 02/36] Updated DisplayShape and SpatialOrientation io --- opencsp/app/sofast/lib/DisplayShape.py | 1 + opencsp/app/sofast/lib/SpatialOrientation.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/opencsp/app/sofast/lib/DisplayShape.py b/opencsp/app/sofast/lib/DisplayShape.py index 291863ff2..01b9c69cc 100644 --- a/opencsp/app/sofast/lib/DisplayShape.py +++ b/opencsp/app/sofast/lib/DisplayShape.py @@ -203,6 +203,7 @@ def load_from_hdf(cls, file: str): else: raise ValueError(f'Model, {data["screen_model"]}, not supported.') + grid_data.update({'screen_model': data['screen_model']}) # Return display object kwargs = { 'name': data['name'], diff --git a/opencsp/app/sofast/lib/SpatialOrientation.py b/opencsp/app/sofast/lib/SpatialOrientation.py index 373d70c1d..0ec5ef3ed 100644 --- a/opencsp/app/sofast/lib/SpatialOrientation.py +++ b/opencsp/app/sofast/lib/SpatialOrientation.py @@ -4,6 +4,8 @@ from opencsp.common.lib.geometry.Vxyz import Vxyz from opencsp.common.lib.tool import hdf5_tools +# TODO: Add HDF5 abstract class inheritance + class SpatialOrientation: """Holds relative orientations of camera, screen, and optic for deflectometry systems""" From 870766ecde78aa4c1bd7d1abcad0b7b8787d3557 Mon Sep 17 00:00:00 2001 From: Braden Date: Wed, 20 Mar 2024 14:51:02 -0600 Subject: [PATCH 03/36] B Modified io, docstrings, and tests of DisplayShape Resolved merge conflicts in DisplayShape and test_Displayshape --- opencsp/app/sofast/lib/DisplayShape.py | 59 +++++++++++-------- .../{test_Display.py => test_DisplayShape.py} | 13 +--- 2 files changed, 36 insertions(+), 36 deletions(-) rename opencsp/app/sofast/test/{test_Display.py => test_DisplayShape.py} (79%) diff --git a/opencsp/app/sofast/lib/DisplayShape.py b/opencsp/app/sofast/lib/DisplayShape.py index 01b9c69cc..c4c176a97 100644 --- a/opencsp/app/sofast/lib/DisplayShape.py +++ b/opencsp/app/sofast/lib/DisplayShape.py @@ -3,12 +3,10 @@ from opencsp.common.lib.geometry.Vxy import Vxy from opencsp.common.lib.geometry.Vxyz import Vxyz -import opencsp.common.lib.tool.hdf5_tools as hdf5_tools +import opencsp.common.lib.tool.hdf5_tools as ht -# TODO: add HDF5 IO interface - -class DisplayShape: +class DisplayShape(ht.HDF5_IO_Abstract): """Representation of a screen/projector for deflectometry.""" def __init__(self, grid_data: dict, name: str = '') -> 'DisplayShape': @@ -168,35 +166,45 @@ def _interp_func_3D(self, uv_display_pts: Vxy, func_xyz) -> Vxyz: return Vxyz(xyz) # meters, display coordinates @classmethod - def load_from_hdf(cls, file: str): - """ - Loads from HDF file + def load_from_hdf(cls, file: str, prefix: str = '') -> 'DisplayShape': + """Loads data from given file. Assumes data is stored as: PREFIX + DisplayShape/Field_1 Parameters ---------- - file : string - HDF5 file to load + file : str + HDF file to save to + prefix : str + Prefix to append to folder path within HDF file (folders must be separated by "/") """ # Load grid data - datasets = ['DisplayShape/screen_model', 'DisplayShape/name'] - data = hdf5_tools.load_hdf5_datasets(datasets, file) + datasets = [ + prefix + 'DisplayShape/screen_model', + prefix + 'DisplayShape/name', + ] + data = ht.load_hdf5_datasets(datasets, file) # Rectangular if data['screen_model'] == 'rectangular2D': datasets = ['DisplayShape/screen_x', 'DisplayShape/screen_y'] - grid_data = hdf5_tools.load_hdf5_datasets(datasets, file) + grid_data = ht.load_hdf5_datasets(datasets, file) # Distorted 2D elif data['screen_model'] == 'distorted2D': - datasets = ['DisplayShape/xy_screen_fraction', 'DisplayShape/xy_screen_coords'] - grid_data = hdf5_tools.load_hdf5_datasets(datasets, file) + datasets = [ + 'DisplayShape/xy_screen_fraction', + 'DisplayShape/xy_screen_coords', + ] + grid_data = ht.load_hdf5_datasets(datasets, file) grid_data['xy_screen_fraction'] = Vxy(grid_data['xy_screen_fraction']) grid_data['xy_screen_coords'] = Vxy(grid_data['xy_screen_coords']) # Distorted 3D elif data['screen_model'] == 'distorted3D': - datasets = ['DisplayShape/xy_screen_fraction', 'DisplayShape/xyz_screen_coords'] - grid_data = hdf5_tools.load_hdf5_datasets(datasets, file) + datasets = [ + 'DisplayShape/xy_screen_fraction', + 'DisplayShape/xyz_screen_coords', + ] + grid_data = ht.load_hdf5_datasets(datasets, file) grid_data['xy_screen_fraction'] = Vxy(grid_data['xy_screen_fraction']) grid_data['xyz_screen_coords'] = Vxyz(grid_data['xyz_screen_coords']) @@ -211,26 +219,25 @@ def load_from_hdf(cls, file: str): } return cls(**kwargs) - def save_to_hdf(self, file: str): - """ - Saves to HDF file + def save_to_hdf(self, file: str, prefix: str = '') -> None: + """Saves data to given file. Data is stored as: PREFIX + DisplayShape/Field_1 Parameters ---------- - file : string - HDF5 file to save - + file : str + HDF file to save to + prefix : str + Prefix to append to folder path within HDF file (folders must be separated by "/") """ - # Get "grid data" datset names and data datasets = [] data = [] for dataset in self.grid_data.keys(): - datasets.append('DisplayShape/' + dataset) - if isinstance(self.grid_data[dataset], Vxy) or isinstance(self.grid_data[dataset], Vxyz): + datasets.append(prefix + 'DisplayShape/' + dataset) + if isinstance(self.grid_data[dataset], (Vxy, Vxyz)): data.append(self.grid_data[dataset].data) else: data.append(self.grid_data[dataset]) # Save data - hdf5_tools.save_hdf5_datasets(data, datasets, file) + ht.save_hdf5_datasets(data, datasets, file) diff --git a/opencsp/app/sofast/test/test_Display.py b/opencsp/app/sofast/test/test_DisplayShape.py similarity index 79% rename from opencsp/app/sofast/test/test_Display.py rename to opencsp/app/sofast/test/test_DisplayShape.py index f6fbefbb6..f53c23f42 100644 --- a/opencsp/app/sofast/test/test_Display.py +++ b/opencsp/app/sofast/test/test_DisplayShape.py @@ -4,7 +4,6 @@ import unittest import numpy as np -from scipy.spatial.transform import Rotation from opencsp.app.sofast.lib.DisplayShape import DisplayShape from opencsp.common.lib.geometry.Vxy import Vxy @@ -61,10 +60,8 @@ def setUpClass(cls): def test_rectangular2D(self): # Instantiate display object - v_cam_screen_screen = Vxyz((0, 0, 1)) - r_screen_cam = Rotation.from_rotvec(np.array([0.0, 0.0, 0.0])) name = 'Test DisplayShape' - disp = DisplayShape(v_cam_screen_screen, r_screen_cam, self.grid_data_rect2D, name) + disp = DisplayShape(self.grid_data_rect2D, name) # Perform calculation calc = disp.interp_func(self.test_Vxy_pts) @@ -74,10 +71,8 @@ def test_rectangular2D(self): def test_distorted2D(self): # Instantiate display object - v_cam_screen_screen = Vxyz((0, 0, 1)) - r_screen_cam = Rotation.from_rotvec(np.array([0.0, 0.0, 0.0])) name = 'Test DisplayShape' - disp = DisplayShape(v_cam_screen_screen, r_screen_cam, self.grid_data_2D, name) + disp = DisplayShape(self.grid_data_2D, name) # Perform calculation calc = disp.interp_func(self.test_Vxy_pts) @@ -87,10 +82,8 @@ def test_distorted2D(self): def test_distorted3D(self): # Instantiate display object - v_cam_screen_screen = Vxyz((0, 0, 1)) - r_screen_cam = Rotation.from_rotvec(np.array([0.0, 0.0, 0.0])) name = 'Test DisplayShape' - disp = DisplayShape(v_cam_screen_screen, r_screen_cam, self.grid_data_3D, name) + disp = DisplayShape(self.grid_data_3D, name) # Perform calculation calc = disp.interp_func(self.test_Vxy_pts) From 058a2606b1d7e35596332ebf24b01bebf813efa8 Mon Sep 17 00:00:00 2001 From: Braden Date: Wed, 20 Mar 2024 14:55:03 -0600 Subject: [PATCH 04/36] Updating test_integration_undefined Resolved merge conflict in test_integration_undefined --- opencsp/app/sofast/test/test_integration_undefined.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/opencsp/app/sofast/test/test_integration_undefined.py b/opencsp/app/sofast/test/test_integration_undefined.py index 95a464cf1..f62b0f731 100644 --- a/opencsp/app/sofast/test/test_integration_undefined.py +++ b/opencsp/app/sofast/test/test_integration_undefined.py @@ -8,8 +8,9 @@ from opencsp.app.sofast.lib.DisplayShape import DisplayShape as Display from opencsp.app.sofast.lib.ImageCalibrationScaling import ImageCalibrationScaling -from opencsp.app.sofast.lib.MeasurementSofastFringe import MeasurementSofastFringe as Measurement -from opencsp.app.sofast.lib.ProcessSofastFringe import ProcessSofastFringe as Sofast +from opencsp.app.sofast.lib.MeasurementSofastFringe import MeasurementSofastFringe +from opencsp.app.sofast.lib.ProcessSofastFringe import ProcessSofastFringe +from opencsp.app.sofast.lib.SpatialOrientation import SpatialOrientation from opencsp.common.lib.camera.Camera import Camera from opencsp.common.lib.deflectometry.Surface2DParabolic import Surface2DParabolic from opencsp.common.lib.opencsp_path.opencsp_root_path import opencsp_code_dir @@ -28,7 +29,8 @@ def test_undefined(self): # Load data camera = Camera.load_from_hdf(file_dataset) display = Display.load_from_hdf(file_dataset) - measurement = Measurement.load_from_hdf(file_measurement) + orientation = SpatialOrientation.load_from_hdf(file_dataset) + measurement = MeasurementSofastFringe.load_from_hdf(file_measurement) calibration = ImageCalibrationScaling.load_from_hdf(file_dataset) # Calibrate measurement @@ -60,7 +62,7 @@ def test_undefined(self): params = load_hdf5_datasets(datasets, file_dataset) # Instantiate sofast object - sofast = Sofast(measurement, camera, display) + sofast = ProcessSofastFringe(measurement, orientation, camera, display) # Update parameters sofast.params.mask_hist_thresh = params['mask_hist_thresh'] From d8a1638d5a83c3c84d52dcce19ef14ce66a3bcb8 Mon Sep 17 00:00:00 2001 From: Braden Date: Wed, 20 Mar 2024 15:30:47 -0600 Subject: [PATCH 05/36] Updated calibrate screen shape example Fixed merge conflicts after rebase. --- .../example_calibration_camera_pose.py | 19 ++++++-- .../example_calibration_screen_shape.py | 4 +- .../app/sofast/lib/CalibrateDisplayShape.py | 21 ++++++-- .../test/test_save_DisplayShape_file.py | 45 ------------------ .../calculations_undefined_mirror/data.h5 | Bin 1077738 -> 1079834 bytes .../spatial_orientation.h5 | Bin 0 -> 4752 bytes 6 files changed, 35 insertions(+), 54 deletions(-) delete mode 100644 opencsp/app/sofast/test/test_save_DisplayShape_file.py create mode 100644 opencsp/test/data/measurements_sofast_fringe/spatial_orientation.h5 diff --git a/example/sofast_fringe/example_calibration_camera_pose.py b/example/sofast_fringe/example_calibration_camera_pose.py index 8ee45e296..4ddc9c6dd 100644 --- a/example/sofast_fringe/example_calibration_camera_pose.py +++ b/example/sofast_fringe/example_calibration_camera_pose.py @@ -1,7 +1,9 @@ from os.path import join, dirname import numpy as np +from scipy.spatial.transform import Rotation +from opencsp.app.sofast.lib.SpatialOrientation import SpatialOrientation from opencsp.common.lib.deflectometry.CalibrationCameraPosition import CalibrationCameraPosition from opencsp.common.lib.camera.Camera import Camera from opencsp.common.lib.geometry.Vxyz import Vxyz @@ -13,8 +15,8 @@ def run_camera_position_calibration(save_dir): """Calibrates the position of the Sofast camera. Saves the rvec/tvec that - define the relative pose of the camera/screen to a CSV file located - at ./data/output/camera_rvec_tvec.csv + define the relative pose of the camera/screen in a SpatialOrientation file + at ./data/output/spatial_orientation.h5 """ # Define directory where screen shape calibration data is saved base_dir_sofast_cal = join(opencsp_code_dir(), 'common/lib/deflectometry/test/data/data_measurement') @@ -43,8 +45,19 @@ def run_camera_position_calibration(save_dir): lt.info(f'Saving figure to: {file:s}') fig.savefig(file) + # Get orientation + r_screen_cam, v_cam_screen_screen = cal.get_data() + r_screen_cam = Rotation.from_rotvec(r_screen_cam) + v_cam_screen_screen = Vxyz(v_cam_screen_screen) + + r_cam_screen = r_screen_cam.inv() + v_cam_screen_cam = v_cam_screen_screen.rotate(r_screen_cam) + + # Create spatial orientation object + orientation = SpatialOrientation(r_cam_screen, v_cam_screen_cam) + # Save data - cal.save_data_as_csv(join(save_dir, 'camera_rvec_tvec.csv')) + orientation.save_to_hdf(join(save_dir, 'spatial_orientation.h5')) def example_driver(): diff --git a/example/sofast_fringe/example_calibration_screen_shape.py b/example/sofast_fringe/example_calibration_screen_shape.py index 28b910939..91ffd66bf 100644 --- a/example/sofast_fringe/example_calibration_screen_shape.py +++ b/example/sofast_fringe/example_calibration_screen_shape.py @@ -56,8 +56,8 @@ def run_screen_shape_calibration(save_dir): cal.make_figures = True cal.run_calibration() - # Save screen shape data as HDF5 file - cal.save_data_as_hdf(join(save_dir, 'screen_distortion_data.h5')) + # Get screen shape data + grid_data = cal.get_data() # Save calibration figures for fig in cal.figures: diff --git a/opencsp/app/sofast/lib/CalibrateDisplayShape.py b/opencsp/app/sofast/lib/CalibrateDisplayShape.py index 5a07650a7..4a8c2e2fe 100644 --- a/opencsp/app/sofast/lib/CalibrateDisplayShape.py +++ b/opencsp/app/sofast/lib/CalibrateDisplayShape.py @@ -335,7 +335,20 @@ def get_data(self) -> dict: self.data_calculation.pts_xyz_screen_aligned.data[:, self.data_calculation.intersection_points_mask] ) - return {'pts_xy_screen_fraction': pts_xy_screen_fraction, 'pts_xyz_screen_coords': pts_xyz_screen} + return {'xy_screen_fraction': pts_xy_screen_fraction, 'xyz_screen_coords': pts_xyz_screen} + + def to_DisplayShape_file(self, file: str, name: str) -> None: + """Saves data to DisplayShape hdf file using the distorted_3d model + + Parameters + ---------- + file : str + Save file name. + name : str + Name of DisplayShape. + """ + grid_data = self.get_data() + return def save_data_as_hdf(self, file: str) -> None: """Saves distortion data to given HDF file""" @@ -344,9 +357,9 @@ def save_data_as_hdf(self, file: str) -> None: # Save distortion data with h5py.File(file, 'w') as f: - f.create_dataset('pts_xy_screen_fraction', data=data['pts_xy_screen_fraction'].data) - f.create_dataset('pts_xyz_screen_coords', data=data['pts_xyz_screen_coords'].data) - lt.info(f'Saved distortion data to: {os.path.abspath(file):s}') + f.create_dataset('xy_screen_fraction', data=data['xy_screen_fraction'].data) + f.create_dataset('xyz_screen_coords', data=data['xyz_screen_coords'].data) + print(f'Saved distortion data to: {os.path.abspath(file):s}') def visualize_located_cameras(self) -> None: """Plots cameras and alignment points""" diff --git a/opencsp/app/sofast/test/test_save_DisplayShape_file.py b/opencsp/app/sofast/test/test_save_DisplayShape_file.py deleted file mode 100644 index 71d77bdc7..000000000 --- a/opencsp/app/sofast/test/test_save_DisplayShape_file.py +++ /dev/null @@ -1,45 +0,0 @@ -from os.path import join -import unittest - -import numpy as np - -from opencsp.app.sofast.lib.save_DisplayShape_file import save_DisplayShape_file -from opencsp.common.lib.tool.hdf5_tools import load_hdf5_datasets -from opencsp.common.lib.geometry.Vxy import Vxy -from opencsp.common.lib.geometry.Vxyz import Vxyz -from opencsp.common.lib.opencsp_path.opencsp_root_path import opencsp_code_dir -import opencsp.common.lib.tool.file_tools as ft - - -class test_save_physical_setup_file(unittest.TestCase): - def test_save_physical_setup_file(self): - """Loads data and saves test Display file""" - # Define input file directory - dir_input_sofast = join(opencsp_code_dir(), 'app/sofast/test/data/data_expected') - dir_input_def = join(opencsp_code_dir(), 'common/lib/deflectometry/test/data/data_expected') - dir_output = join(opencsp_code_dir(), 'app/sofast/test/data/output') - file_save = join(dir_output, 'test_physical_setup_file.h5') - - ft.create_directories_if_necessary(dir_output) - - # Define data files - file_screen_distortion_data = join(dir_input_sofast, 'screen_distortion_data_100_100.h5') - file_cam = join(dir_input_def, 'camera_rvec_tvec.csv') - - # Load data - name = 'Test Physical Setup File' - data_dist = load_hdf5_datasets(['pts_xy_screen_fraction', 'pts_xyz_screen_coords'], file_screen_distortion_data) - screen_distortion_data = { - 'pts_xy_screen_fraction': Vxy(data_dist['pts_xy_screen_fraction']), - 'pts_xyz_screen_coords': Vxyz(data_dist['pts_xyz_screen_coords']), - } - data_cam = np.loadtxt(file_cam, delimiter=',') - rvec = data_cam[0] - tvec = data_cam[1] - - # Save physical setup file - save_DisplayShape_file(screen_distortion_data, name, rvec, tvec, file_save) - - -if __name__ == '__main__': - unittest.main() diff --git a/opencsp/test/data/measurements_sofast_fringe/calculations_undefined_mirror/data.h5 b/opencsp/test/data/measurements_sofast_fringe/calculations_undefined_mirror/data.h5 index 941c48f81e84a11e7bcba1d32d42af8be625e39c..0782206b046770c687c9e229a15733f9ee935b10 100644 GIT binary patch delta 493 zcmaF$*m2eu#|autQdJYR+NKA6=FHo8po?+x1~mc3hRr*fbeS31CO>CAshq(80ZkNR9)DIf0l9h`E87 z2Z(urm=B2gw>z*4EYcAVE=VlNOw92w%1q5G0W$ORz!pxomtr!WexZa*V7rZpK%NAn z!SvNW0`dkGQ0G7#!@y9ST$Gxc7oVG-l9~gOf{9B&)ouUfBf!VV$TFSNPe5~el%Igc z^i_TW3nm^2*zV;ous~^Yfsz8JRw+cA#Kw*P87CL8a!j8f$;4r*1q}kIQ4B@#$%(n~ z5F0^aWk`Gw6Rd9fSxF{sQ1Eagh1hgge}R0?SCvp}Cg0|iWnKjeNZH97G&z`y$^^C_ zEau{8oNf~!P{GMm1$WMO#)$|1865a(_-&!ny#0T(4IGc9UbA1fV((=$%a;9nJ}!?q ZyGzhv%ObV@b3Ci-vQ delta 230 zcmbRB#qrf+#|autuL>t>wJ`>4+{n(vn6Y^$lP)tO>*VLGCns*Jn5@7o(d@w9?!e9n z#7scU48$xz%nHP8Kn#-O0Afxc<^p1FAm#yLULfWJV*c$8>;j8)I2jorz+>V@`$?V( z+r3N#5+oQarmyx9ke4qiOHGb1PA*DK&5KV?%w>QwbMsSDbAZ~WZ@k4Xxt+;ZfQ^xn sZ91o)fF`#9)HpAHfr%SsryKbRXiPr9!@+D70Om*e2`tzSlv|(#05%0cYybcN diff --git a/opencsp/test/data/measurements_sofast_fringe/spatial_orientation.h5 b/opencsp/test/data/measurements_sofast_fringe/spatial_orientation.h5 new file mode 100644 index 0000000000000000000000000000000000000000..8dffffb76d624b1daee3b5fadfa29ff5cb58c412 GIT binary patch literal 4752 zcmeD5aB<`1lHy_j0S*oZ76t(@6Gr@pf(b$p5f~pPp8#brLg@}Dy@CnCU}OM61_lYJ zxFFPgbaf#?uC5F~l`!*RG*lbI16Bx&112y^kEjsvaCHm-c{l>0CFbM(l z^lJe1R|hoxz`R)$pPZN*Uz}W&nhH+2Wk`Gw6Q&N9o;6?sIOt$Me-}_1hLxKN&~yva zkDiWk7&GF985vl?^%#Tz)l`t2%*Y4{NocS#AZQ7&Qb7fKXxKY2m~bc!#Q`K;F~QT720MP|!FTmPKm)=Xh4zcQBn6lQ?$Cex!xZsDDR8U^E0q tLtr!nMnhmYh5%-}64Ih)gft7`?MYaB9oD`?@X2jw&cM;mtQgQ>1^^txcHRI0 literal 0 HcmV?d00001 From ac8678206b48c19a043ba0392099df06be6abd92 Mon Sep 17 00:00:00 2001 From: Braden Date: Wed, 20 Mar 2024 19:14:41 -0600 Subject: [PATCH 06/36] Added saving io tests to DisplayShape --- opencsp/app/sofast/lib/DisplayShape.py | 4 ++ opencsp/app/sofast/test/test_DisplayShape.py | 39 +++++++++++++++++++- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/opencsp/app/sofast/lib/DisplayShape.py b/opencsp/app/sofast/lib/DisplayShape.py index c4c176a97..7ffdc9166 100644 --- a/opencsp/app/sofast/lib/DisplayShape.py +++ b/opencsp/app/sofast/lib/DisplayShape.py @@ -239,5 +239,9 @@ def save_to_hdf(self, file: str, prefix: str = '') -> None: else: data.append(self.grid_data[dataset]) + # Add name + datasets.append(prefix + 'DisplayShape/name') + data.append(self.name) + # Save data ht.save_hdf5_datasets(data, datasets, file) diff --git a/opencsp/app/sofast/test/test_DisplayShape.py b/opencsp/app/sofast/test/test_DisplayShape.py index f53c23f42..738b54d1c 100644 --- a/opencsp/app/sofast/test/test_DisplayShape.py +++ b/opencsp/app/sofast/test/test_DisplayShape.py @@ -2,6 +2,7 @@ """ import unittest +from os.path import dirname, join import numpy as np @@ -10,7 +11,7 @@ from opencsp.common.lib.geometry.Vxyz import Vxyz -class TestDisplay(unittest.TestCase): +class TestDisplayShape(unittest.TestCase): @classmethod def setUpClass(cls): # Define screen X and Y extent @@ -91,6 +92,42 @@ def test_distorted3D(self): # Test np.testing.assert_allclose(calc.data, self.exp_Vxyz_disp_pts.data, rtol=0, atol=1e-7) + def test_save_load_hdf_dist_3d(self): + # Instantiate display object + name = 'Test DisplayShape' + disp = DisplayShape(self.grid_data_3D, name) + file = join(dirname(__file__), 'data/output/test_display_shape_dist_3d.h5') + + # Save + disp.save_to_hdf(file) + + # Load + DisplayShape.load_from_hdf(file) + + def test_save_load_hdf_dist_2d(self): + # Instantiate display object + name = 'Test DisplayShape' + disp = DisplayShape(self.grid_data_2D, name) + file = join(dirname(__file__), 'data/output/test_display_shape_dist_2d.h5') + + # Save + disp.save_to_hdf(file) + + # Load + DisplayShape.load_from_hdf(file) + + def test_save_load_hdf_rectangular(self): + # Instantiate display object + name = 'Test DisplayShape' + disp = DisplayShape(self.grid_data_rect2D, name) + file = join(dirname(__file__), 'data/output/test_display_shape_rect.h5') + + # Save + disp.save_to_hdf(file) + + # Load + DisplayShape.load_from_hdf(file) + if __name__ == '__main__': unittest.main() From d882d75858e89e8f614dd56b71f3f075886e362b Mon Sep 17 00:00:00 2001 From: Braden Date: Wed, 20 Mar 2024 19:16:57 -0600 Subject: [PATCH 07/36] Updated CalibrateDisplayShape to return DisplayShape object directly and updated example file. --- .../example_calibration_screen_shape.py | 11 +++++++++-- opencsp/app/sofast/lib/CalibrateDisplayShape.py | 17 +++++++++-------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/example/sofast_fringe/example_calibration_screen_shape.py b/example/sofast_fringe/example_calibration_screen_shape.py index 91ffd66bf..c6fdb3b33 100644 --- a/example/sofast_fringe/example_calibration_screen_shape.py +++ b/example/sofast_fringe/example_calibration_screen_shape.py @@ -14,7 +14,9 @@ def run_screen_shape_calibration(save_dir): - """Runs screen shape calibration. Saves data to ./data/output/screen_shape""" + """Runs screen shape calibration. Saves a DisplayShape HDF5 file + to ./data/output/screen_shape/display_shape.h5 + """ # Load output data from Scene Reconstruction (Aruco marker xyz points) file_pts_data = join( opencsp_code_dir(), 'common/lib/deflectometry/test/data/data_measurement', 'point_locations.csv' @@ -57,7 +59,12 @@ def run_screen_shape_calibration(save_dir): cal.run_calibration() # Get screen shape data - grid_data = cal.get_data() + display_shape = cal.as_DisplayShape('Example display shape') + + # Save DisplayShape file + file = join(save_dir, 'display_shape.h5') + display_shape.save_to_hdf(file) + lt.info(f'Saved DisplayShape file to {file:s}') # Save calibration figures for fig in cal.figures: diff --git a/opencsp/app/sofast/lib/CalibrateDisplayShape.py b/opencsp/app/sofast/lib/CalibrateDisplayShape.py index 4a8c2e2fe..92dd9f1e4 100644 --- a/opencsp/app/sofast/lib/CalibrateDisplayShape.py +++ b/opencsp/app/sofast/lib/CalibrateDisplayShape.py @@ -15,8 +15,9 @@ from scipy.spatial.transform import Rotation from tqdm import tqdm +from opencsp.app.sofast.lib.DisplayShape import DisplayShape import opencsp.app.sofast.lib.image_processing as ip -from opencsp.app.sofast.lib.MeasurementSofastFringe import MeasurementSofastFringe as Measurement +from opencsp.app.sofast.lib.MeasurementSofastFringe import MeasurementSofastFringe from opencsp.common.lib.camera.Camera import Camera from opencsp.common.lib.deflectometry.ImageProjection import CalParams from opencsp.common.lib.geometry.Vxy import Vxy @@ -44,7 +45,7 @@ class DataInput: Camera object used to capture screen distortion data image_projection_data : dict Image projection parameters - measurements_screen : list[Measurement] + measurements_screen : list[MeasurementSofastFringe] Screen shape Sofast measurement objects assume_located_points : bool To assume that points are located accuratly, does not optimize point location, by default True. @@ -58,7 +59,7 @@ class DataInput: pts_xyz_marker: Vxyz camera: Camera image_projection_data: dict - measurements_screen: list[Measurement] + measurements_screen: list[MeasurementSofastFringe] assume_located_points: bool = True ray_intersection_threshold: float = 0.001 @@ -337,18 +338,18 @@ def get_data(self) -> dict: return {'xy_screen_fraction': pts_xy_screen_fraction, 'xyz_screen_coords': pts_xyz_screen} - def to_DisplayShape_file(self, file: str, name: str) -> None: - """Saves data to DisplayShape hdf file using the distorted_3d model + def as_DisplayShape(self, name: str) -> DisplayShape: + """Saves data to DisplayShape hdf file using the distorted3d model, see + DisplayShape documentation for more information. Parameters ---------- - file : str - Save file name. name : str Name of DisplayShape. """ grid_data = self.get_data() - return + grid_data.update({'screen_model': 'distorted3D'}) + return DisplayShape(grid_data, name) def save_data_as_hdf(self, file: str) -> None: """Saves distortion data to given HDF file""" From 94348f4c04f24fa95844549df0a0e57e86e6a929 Mon Sep 17 00:00:00 2001 From: Braden Date: Wed, 20 Mar 2024 19:18:15 -0600 Subject: [PATCH 08/36] Updated documentation in example-calibration-camera-pose --- .../sofast_fringe/example_calibration_camera_pose.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/example/sofast_fringe/example_calibration_camera_pose.py b/example/sofast_fringe/example_calibration_camera_pose.py index 4ddc9c6dd..0231f4077 100644 --- a/example/sofast_fringe/example_calibration_camera_pose.py +++ b/example/sofast_fringe/example_calibration_camera_pose.py @@ -40,11 +40,6 @@ def run_camera_position_calibration(save_dir): cal.make_figures = True cal.run_calibration() - for fig in cal.figures: - file = join(save_dir, fig.get_label() + '.png') - lt.info(f'Saving figure to: {file:s}') - fig.savefig(file) - # Get orientation r_screen_cam, v_cam_screen_screen = cal.get_data() r_screen_cam = Rotation.from_rotvec(r_screen_cam) @@ -59,6 +54,12 @@ def run_camera_position_calibration(save_dir): # Save data orientation.save_to_hdf(join(save_dir, 'spatial_orientation.h5')) + # Save figures + for fig in cal.figures: + file = join(save_dir, fig.get_label() + '.png') + lt.info(f'Saving figure to: {file:s}') + fig.savefig(file) + def example_driver(): # Define save dir From 1412cece961e2ef08e4bd4e2eccaac84fcd6705f Mon Sep 17 00:00:00 2001 From: Braden Date: Wed, 20 Mar 2024 19:19:17 -0600 Subject: [PATCH 09/36] Deleted example-calibration-save-DisplayShape-file --- ...mple_calibration_save_DisplayShape_file.py | 45 ------------------- 1 file changed, 45 deletions(-) delete mode 100644 example/sofast_fringe/example_calibration_save_DisplayShape_file.py diff --git a/example/sofast_fringe/example_calibration_save_DisplayShape_file.py b/example/sofast_fringe/example_calibration_save_DisplayShape_file.py deleted file mode 100644 index 30a071e62..000000000 --- a/example/sofast_fringe/example_calibration_save_DisplayShape_file.py +++ /dev/null @@ -1,45 +0,0 @@ -from os.path import join, dirname - -import numpy as np - -from opencsp.app.sofast.lib.save_DisplayShape_file import save_DisplayShape_file -from opencsp.common.lib.geometry.Vxy import Vxy -from opencsp.common.lib.geometry.Vxyz import Vxyz -from opencsp.common.lib.opencsp_path.opencsp_root_path import opencsp_code_dir -from opencsp.common.lib.tool.hdf5_tools import load_hdf5_datasets -import opencsp.common.lib.tool.file_tools as ft - - -def example_save_display_shape_file(): - """Example script that saves a DisplayShape file from its components""" - # Define save directory - save_dir = join(dirname(__file__), 'data/output/save_DisplayShape_file') - ft.create_directories_if_necessary(save_dir) - - # Load screen distortion data - file_screen_distortion_data = join( - opencsp_code_dir(), 'app/sofast/test/data/data_expected', 'screen_distortion_data_100_100.h5' - ) - datasets = ['pts_xy_screen_fraction', 'pts_xyz_screen_coords'] - data = load_hdf5_datasets(datasets, file_screen_distortion_data) - screen_distortion_data = { - 'pts_xy_screen_fraction': Vxy(data['pts_xy_screen_fraction']), - 'pts_xyz_screen_coords': Vxyz(data['pts_xyz_screen_coords']), - } - - # Load rvec and tvec - file_rvec_tvec = join( - opencsp_code_dir(), 'common/lib/deflectometry/test/data/data_expected', 'camera_rvec_tvec.csv' - ) - pose_data = np.loadtxt(file_rvec_tvec, delimiter=',') - rvec = pose_data[0] - tvec = pose_data[1] - - # Save DisplayShape file - name = 'Example physical setup file' - file_save = join(save_dir, 'example_display_shape_file.h5') - save_DisplayShape_file(screen_distortion_data, name, rvec, tvec, file_save) - - -if __name__ == '__main__': - example_save_display_shape_file() From 9f76a5927e0e3efde1fb5d8f844798ab1e6a3f29 Mon Sep 17 00:00:00 2001 From: Braden Date: Wed, 20 Mar 2024 21:41:22 -0600 Subject: [PATCH 10/36] Deleted save_DisplayShape file --- .../app/sofast/lib/save_DisplayShape_file.py | 47 ------------------- 1 file changed, 47 deletions(-) delete mode 100644 opencsp/app/sofast/lib/save_DisplayShape_file.py diff --git a/opencsp/app/sofast/lib/save_DisplayShape_file.py b/opencsp/app/sofast/lib/save_DisplayShape_file.py deleted file mode 100644 index 509ecfa49..000000000 --- a/opencsp/app/sofast/lib/save_DisplayShape_file.py +++ /dev/null @@ -1,47 +0,0 @@ -"""Script that saves a Sofast physical setup file from previously processed data -""" - -from numpy import ndarray -from scipy.spatial.transform import Rotation - -from opencsp.app.sofast.lib.DisplayShape import DisplayShape -from opencsp.common.lib.geometry.Vxy import Vxy -from opencsp.common.lib.geometry.Vxyz import Vxyz - - -def save_DisplayShape_file( - screen_distortion_data: dict, name: str, rvec: ndarray, tvec: ndarray, file_save: str -) -> None: - """Constructs and saves DisplayShape file - - Parameters - ---------- - screen_distortion_data : dict - Dict with following fields: 1) pts_xy_screen_fraction: Vxy, 2) pts_xyz_screen_coords: Vxyz - name : str - DisplayShape name - rvec : ndarray - Screen to camera rotation vector - tvec : ndarray - Camera to screen (in screen coordinates) translation vector - file_save : str - Output display file name to save to - """ - # Load screen distortion data - pts_xy_screen_fraction: Vxy = screen_distortion_data['pts_xy_screen_fraction'] - pts_xyz_screen_coords: Vxyz = screen_distortion_data['pts_xyz_screen_coords'] - - # Gather rvec and tvec - rot_screen_cam = Rotation.from_rotvec(rvec) - v_cam_screen_screen = Vxyz(tvec) - - # Gather display grid data - grid_data = dict( - screen_model='distorted3D', xy_screen_fraction=pts_xy_screen_fraction, xyz_screen_coords=pts_xyz_screen_coords - ) - - # Create display object - display = DisplayShape(v_cam_screen_screen, rot_screen_cam, grid_data, name) - - # Save to HDF file - display.save_to_hdf(file_save) From b95fcdff51d92a5ca86377c3fc5f1f238b2fd20c Mon Sep 17 00:00:00 2001 From: Braden Date: Wed, 20 Mar 2024 21:41:33 -0600 Subject: [PATCH 11/36] Updated test_CalibrateDisplayShape unit test --- .../sofast/test/test_CalibrateDisplayShape.py | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/opencsp/app/sofast/test/test_CalibrateDisplayShape.py b/opencsp/app/sofast/test/test_CalibrateDisplayShape.py index c2d7c16c1..b44098b8b 100644 --- a/opencsp/app/sofast/test/test_CalibrateDisplayShape.py +++ b/opencsp/app/sofast/test/test_CalibrateDisplayShape.py @@ -11,7 +11,7 @@ from opencsp.common.lib.opencsp_path.opencsp_root_path import opencsp_code_dir from opencsp.app.sofast.lib.CalibrateDisplayShape import CalibrateDisplayShape, DataInput -from opencsp.app.sofast.lib.MeasurementSofastFringe import MeasurementSofastFringe as Measurement +from opencsp.app.sofast.lib.MeasurementSofastFringe import MeasurementSofastFringe from opencsp.common.lib.camera.Camera import Camera from opencsp.common.lib.deflectometry.ImageProjection import ImageProjection from opencsp.common.lib.geometry.Vxyz import Vxyz @@ -21,12 +21,10 @@ class TestCalibrateDisplayShape(unittest.TestCase): + """Tests CalibrateDisplayShape""" + @classmethod def setUpClass(cls): - """Tests the CalibrateDisplayShape process. If directories are None, - uses default test data directory. All input files listed below must be - on the dir_input path. - """ # Define default data directories dir_input_sofast = join(opencsp_code_dir(), 'app/sofast/test/data/data_measurement') dir_input_def = join(opencsp_code_dir(), 'common/lib/deflectometry/test/data/data_measurement') @@ -56,7 +54,7 @@ def setUpClass(cls): pts_xyz_marker, camera, image_projection_data, - [Measurement.load_from_hdf(f) for f in files_screen_shape_measurement], + [MeasurementSofastFringe.load_from_hdf(f) for f in files_screen_shape_measurement], ) # Perform screen position calibration @@ -73,18 +71,26 @@ def setUpClass(cls): cls.data_meas = dist_data @pytest.mark.skipif(os.name != 'nt', reason='Does not pass in Linux environment for unkonwn reason.') - def test_screen_distortion_data(self): - """Tests screen calibration data""" + def test_xy_screen_fraction(self): + """Tests xy points""" np.testing.assert_allclose( - self.data_meas['pts_xy_screen_fraction'].data, self.data_exp['pts_xy_screen_fraction'], rtol=0, atol=1e-6 + self.data_meas['xy_screen_fraction'].data, self.data_exp['pts_xy_screen_fraction'], rtol=0, atol=1e-6 ) + + @pytest.mark.skipif(os.name != 'nt', reason='Does not pass in Linux environment for unkonwn reason.') + def test_xyz_screen_coords(self): + """Tests xyz points""" np.testing.assert_allclose( - self.data_meas['pts_xyz_screen_coords'].data, self.data_exp['pts_xyz_screen_coords'], rtol=0, atol=1e-6 + self.data_meas['xyz_screen_coords'].data, self.data_exp['pts_xyz_screen_coords'], rtol=0, atol=1e-6 ) if __name__ == '__main__': + # Set up save dir save_dir = join(dirname(__file__), 'data/output') ft.create_directories_if_necessary(save_dir) + + # Set up logger lt.logger(join(save_dir, 'log_display_shape.txt'), lt.log.WARN) + unittest.main() From 7b217ce1924fe1c4e96f74fb89c18b83a4ad1cc7 Mon Sep 17 00:00:00 2001 From: Braden Date: Wed, 20 Mar 2024 21:45:12 -0600 Subject: [PATCH 12/36] Added IO test to calibrate display shape unit test --- .../sofast/test/test_CalibrateDisplayShape.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/opencsp/app/sofast/test/test_CalibrateDisplayShape.py b/opencsp/app/sofast/test/test_CalibrateDisplayShape.py index b44098b8b..60a630eea 100644 --- a/opencsp/app/sofast/test/test_CalibrateDisplayShape.py +++ b/opencsp/app/sofast/test/test_CalibrateDisplayShape.py @@ -60,30 +60,35 @@ def setUpClass(cls): # Perform screen position calibration cal_screen_position = CalibrateDisplayShape(data_input) cal_screen_position.run_calibration() - - # Get distortion data - dist_data = cal_screen_position.get_data() + cls.cal = cal_screen_position # Test screen distortion information cls.data_exp = load_hdf5_datasets( ['pts_xy_screen_fraction', 'pts_xyz_screen_coords'], join(dir_output, 'screen_distortion_data_100_100.h5') ) - cls.data_meas = dist_data @pytest.mark.skipif(os.name != 'nt', reason='Does not pass in Linux environment for unkonwn reason.') def test_xy_screen_fraction(self): """Tests xy points""" + data_meas = self.cal.get_data() np.testing.assert_allclose( - self.data_meas['xy_screen_fraction'].data, self.data_exp['pts_xy_screen_fraction'], rtol=0, atol=1e-6 + data_meas['xy_screen_fraction'].data, self.data_exp['pts_xy_screen_fraction'], rtol=0, atol=1e-6 ) @pytest.mark.skipif(os.name != 'nt', reason='Does not pass in Linux environment for unkonwn reason.') def test_xyz_screen_coords(self): """Tests xyz points""" + data_meas = self.cal.get_data() np.testing.assert_allclose( - self.data_meas['xyz_screen_coords'].data, self.data_exp['pts_xyz_screen_coords'], rtol=0, atol=1e-6 + data_meas['xyz_screen_coords'].data, self.data_exp['pts_xyz_screen_coords'], rtol=0, atol=1e-6 ) + def test_save_display_object(self): + """Tests saving DisplayShape object""" + display_shape = self.cal.as_DisplayShape('Test display') + file = join(dirname(__file__), 'data/output/test_calibration_display.h5') + display_shape.save_to_hdf(file) + if __name__ == '__main__': # Set up save dir From 48fed69a45e21db4cf8a1426f13a43eae4d6ea97 Mon Sep 17 00:00:00 2001 From: Braden Date: Wed, 20 Mar 2024 21:53:31 -0600 Subject: [PATCH 13/36] Updated HDF5 test data files to contain SpatialOrientation. --- .../calculations_facet/data.h5 | Bin 1590688 -> 1592784 bytes .../calculations_facet/data_distorted_3d.h5 | Bin 1592624 -> 1594720 bytes .../calculations_facet/data_no_ls.h5 | Bin 1590688 -> 1592784 bytes .../calculations_facet/data_plano.h5 | Bin 1590320 -> 1592416 bytes .../calculations_facet/data_rectangular.h5 | Bin 1582960 -> 1585056 bytes .../display_distorted_2d.h5 | Bin 18232 -> 18232 bytes .../display_distorted_3d.h5 | Bin 20056 -> 20056 bytes .../display_rectangular.h5 | Bin 12264 -> 12264 bytes 8 files changed, 0 insertions(+), 0 deletions(-) diff --git a/opencsp/test/data/measurements_sofast_fringe/calculations_facet/data.h5 b/opencsp/test/data/measurements_sofast_fringe/calculations_facet/data.h5 index e91bf83221600c07b1dc21fe021feaa762887a60..19344c8d39d48328f42d8f9510d0b13603a033da 100644 GIT binary patch delta 450 zcmZ3`op@n(;sgz*3%(Pz+NLx8CAshq(80Tvz- zU3(6-4uq$vrfI3-XNtc19*xR+(8G)Dy zh?#+y1&CRJm<@C eu07zK01yiTu@Ddo1F;AYivqFO_JDKZ0p$QNbuKRe diff --git a/opencsp/test/data/measurements_sofast_fringe/calculations_facet/data_distorted_3d.h5 b/opencsp/test/data/measurements_sofast_fringe/calculations_facet/data_distorted_3d.h5 index 97f2687a598c363e4823184090938d5997eed418..9661d12442466abcf92fe0c26b39bc48680d357e 100644 GIT binary patch delta 460 zcmdlmEAhdq#0eTq3853U+NLx8CAshq(80Vn(= zzzjwx-@q5b5Ac_mxG{9H0<%Q31ADszJ0lP?0WmWWvj8zG5VHX>NR9)DIf0l9h`E87 z2Z(urm=B2gw>z*46r55BE=VlNOw92w%1q5G0W$ORAQr;xo!n@s0di$K$0ZOJ1Y#i| z76xJwAQlB;vF#j}#5Jm?e^6mkk^-IHq%46)58LfEls*qR3Y!g9Bd;zb$l{xBqXpf#b2%Yxe6_?7eJe g*|LAn$K?@ccL_RdS)|r~j%T%f2h(XWiDQ@S0m)f>cK`qY delta 160 zcmaE`DsjWC#0eTq2EG%u+86^iZe(X-%-Fn>NtcrFA(zqG5>Z4c7cLZoQw<*;4yKd{p1G* e0__}^Kv)ong@9NXh(&-{6o|#Pb6gVFs0IMEmo7#C diff --git a/opencsp/test/data/measurements_sofast_fringe/calculations_facet/data_no_ls.h5 b/opencsp/test/data/measurements_sofast_fringe/calculations_facet/data_no_ls.h5 index 4825f831f46a5bfabf923277c954189048176ac0..4f72d8d7ac1c5ea786da8f7bb03d972719d96a51 100644 GIT binary patch delta 449 zcmZ3`op@n(;sgz*3%(Pz+NLx8CAshq(80Tvz- zU3(6-4uq$vrfI3-XNtc19*xR+(8G)Dy zh?#+y1&CRJm<@zz)^n%j@lFg3n?T+k>K+FWh%s|Wn#H>Kf2E-sa4j|?PVlE)& z24Wr{<^^ItAm-oh$Sz=hR3W$^u_QAw$G<2uHLnE7%+G_^2(x%{qoGDS(49am2*g4_ zEDXdVKr9NxV%ra#5icm89Kf!?S>O(Fqr}FI{~0GY@Cq0dKz#xA1Vd4La$;_LadJ^= zD%hGbBtD2a`2erMWCdmkZY1wc|9oB`U(>(`YBtO$FdgjY@50Ey#J~X+{QwFb*~t%# zIhc2N0G+|hIDNud0fp%fy#i639xx*|8=8J)GC1(n@Y_PCdHern8#o?Iy=K2|#oo(i hmM#1Dd|V!Jc9)>TmPKm)=Xh4zcQBn6lQ?$C9st_+gi!zh delta 157 zcmaDbBXL7V;sgyQ1DA4 zG9*5TIr#vu0mx_ENS>W;d{7`?vp^qW5~_K@e*P|u3``6hPzwySfMFv$`GGMflQz&5 zyo}R14hvLqcEB`mz9{mQ$>6|O!*2_n=I#HRZQyt;^_u;<6?-q6S+?xo^Kp5^* delta 159 zcmZ2*KjFi8S%F!i*@3;? zft?YEnShuXh*^M`6^Pk@7$nC5#GF9P1;pGy%mc){K+Ffk{M#Ma1!ipLWMqH0NmmdWoqH77f8X-uBLwP5oDE)Q$|2nGnyfKZ#w-541G D>AVf& delta 71 zcmdnd$GD@9aYF?wW5ni4)>0Nmw#n}~HMs;BAYh05#Eo*3CAc)W6rds}6hI;lTnjcE QaR0EHJcHF>a)jFh0JZNFC;$Ke diff --git a/opencsp/test/data/measurements_sofast_fringe/display_distorted_3d.h5 b/opencsp/test/data/measurements_sofast_fringe/display_distorted_3d.h5 index 5b2321a542af9fe35c9369f0be165a8de8250965..7ade1407ec31317483c89b1574aad2160196e346 100644 GIT binary patch delta 56 zcmcaHhw;W7#tjv$i~^f0SxZ?MSth^d)ST?Vr7?K|*MiLtxH6phBN!k+143;M_hn=R E00C4EjQ{`u delta 71 zcmcaHhw;W7#tjv$j1ikFSxZ?M*(Sf|)Z`LifPfwH6F16Dmf+IhQh Date: Wed, 20 Mar 2024 21:53:42 -0600 Subject: [PATCH 14/36] Fixed test spatial orientation --- opencsp/app/sofast/test/test_SpatialOrientation.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/opencsp/app/sofast/test/test_SpatialOrientation.py b/opencsp/app/sofast/test/test_SpatialOrientation.py index 85b3480d6..a735d9d34 100644 --- a/opencsp/app/sofast/test/test_SpatialOrientation.py +++ b/opencsp/app/sofast/test/test_SpatialOrientation.py @@ -1,5 +1,4 @@ -"""Unit test suite to test the SpatialOrientation class -""" +"""Unit test suite to test the SpatialOrientation class""" import os import unittest @@ -7,7 +6,6 @@ import numpy as np from scipy.spatial.transform import Rotation -from opencsp.app.sofast.lib.DisplayShape import DisplayShape as Display from opencsp.app.sofast.lib.SpatialOrientation import SpatialOrientation from opencsp.common.lib.geometry.Vxyz import Vxyz from opencsp.common.lib.opencsp_path.opencsp_root_path import opencsp_code_dir @@ -23,18 +21,18 @@ def setUpClass(cls): # Define test data files for single facet processing data_file_facet = os.path.join(base_dir, 'calculations_facet/data.h5') - # Create spatial orientation objects + # Load data datasets = [ 'DataSofastCalculation/geometry/general/r_optic_cam_refine_1', 'DataSofastCalculation/geometry/general/v_cam_optic_cam_refine_2', ] - # Load data - data = load_hdf5_datasets(datasets, data_file_facet) - display = Display.load_from_hdf(data_file_facet) + ori = SpatialOrientation.load_from_hdf(data_file_facet) + + data = load_hdf5_datasets(datasets, data_file_facet) r_cam_optic = Rotation.from_rotvec(data['r_optic_cam_refine_1']).inv() v_cam_optic_cam = Vxyz(data['v_cam_optic_cam_refine_2']) - ori = SpatialOrientation(display.r_cam_screen, display.v_cam_screen_cam) + ori.orient_optic_cam(r_cam_optic, v_cam_optic_cam) # Save spatial orientation From c92d96eded9d34af6f604e677bf5df6bc17911be Mon Sep 17 00:00:00 2001 From: Braden Date: Wed, 20 Mar 2024 21:55:55 -0600 Subject: [PATCH 15/36] Updated test integration single facet --- opencsp/app/sofast/test/test_integration_single_facet.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/opencsp/app/sofast/test/test_integration_single_facet.py b/opencsp/app/sofast/test/test_integration_single_facet.py index 11c792162..e9720b96e 100644 --- a/opencsp/app/sofast/test/test_integration_single_facet.py +++ b/opencsp/app/sofast/test/test_integration_single_facet.py @@ -12,6 +12,7 @@ from opencsp.app.sofast.lib.ImageCalibrationScaling import ImageCalibrationScaling from opencsp.app.sofast.lib.MeasurementSofastFringe import MeasurementSofastFringe as Measurement from opencsp.app.sofast.lib.ProcessSofastFringe import ProcessSofastFringe as Sofast +from opencsp.app.sofast.lib.SpatialOrientation import SpatialOrientation from opencsp.common.lib.camera.Camera import Camera from opencsp.common.lib.deflectometry.Surface2DPlano import Surface2DPlano from opencsp.common.lib.deflectometry.Surface2DParabolic import Surface2DParabolic @@ -55,6 +56,7 @@ def setUpClass(cls, base_dir: str | None = None): for file_dataset in cls.files_dataset: # Load display camera = Camera.load_from_hdf(file_dataset) + orientation = SpatialOrientation.load_from_hdf(file_dataset) calibration = ImageCalibrationScaling.load_from_hdf(file_dataset) display = Display.load_from_hdf(file_dataset) @@ -111,7 +113,7 @@ def setUpClass(cls, base_dir: str | None = None): params = load_hdf5_datasets(datasets, file_dataset) # Instantiate sofast object - sofast = Sofast(measurement, camera, display) + sofast = Sofast(measurement, orientation, camera, display) # Update parameters sofast.params.mask_hist_thresh = params['mask_hist_thresh'] From efd2a6a736a5b2033e12041a19b9e42e0025e5f7 Mon Sep 17 00:00:00 2001 From: Braden Date: Wed, 20 Mar 2024 22:02:49 -0600 Subject: [PATCH 16/36] Updated test integration multifacet and assiciated data file --- .../test/test_integration_multi_facet.py | 14 ++++++++------ .../test/test_integration_single_facet.py | 12 ++++++------ .../calculations_facet_ensemble/data.h5 | Bin 4984830 -> 4986926 bytes 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/opencsp/app/sofast/test/test_integration_multi_facet.py b/opencsp/app/sofast/test/test_integration_multi_facet.py index f5c47bf58..79362fe11 100644 --- a/opencsp/app/sofast/test/test_integration_multi_facet.py +++ b/opencsp/app/sofast/test/test_integration_multi_facet.py @@ -7,12 +7,13 @@ from scipy.spatial.transform import Rotation import numpy as np -from opencsp.app.sofast.lib.DisplayShape import DisplayShape as Display +from opencsp.app.sofast.lib.DisplayShape import DisplayShape from opencsp.app.sofast.lib.DefinitionEnsemble import DefinitionEnsemble from opencsp.app.sofast.lib.DefinitionFacet import DefinitionFacet from opencsp.app.sofast.lib.ImageCalibrationScaling import ImageCalibrationScaling -from opencsp.app.sofast.lib.ProcessSofastFringe import ProcessSofastFringe as Sofast -from opencsp.app.sofast.lib.MeasurementSofastFringe import MeasurementSofastFringe as Measurement +from opencsp.app.sofast.lib.MeasurementSofastFringe import MeasurementSofastFringe +from opencsp.app.sofast.lib.ProcessSofastFringe import ProcessSofastFringe +from opencsp.app.sofast.lib.SpatialOrientation import SpatialOrientation from opencsp.common.lib.camera.Camera import Camera from opencsp.common.lib.deflectometry.Surface2DParabolic import Surface2DParabolic from opencsp.common.lib.geometry.Vxyz import Vxyz @@ -41,8 +42,9 @@ def setUpClass(cls, base_dir: str | None = None): # Load data camera = Camera.load_from_hdf(file_dataset) - display = Display.load_from_hdf(file_dataset) - measurement = Measurement.load_from_hdf(file_measurement) + display = DisplayShape.load_from_hdf(file_dataset) + orientation = SpatialOrientation.load_from_hdf(file_dataset) + measurement = MeasurementSofastFringe.load_from_hdf(file_measurement) calibration = ImageCalibrationScaling.load_from_hdf(file_dataset) # Load sofast params @@ -64,7 +66,7 @@ def setUpClass(cls, base_dir: str | None = None): measurement.calibrate_fringe_images(calibration) # Instantiate sofast object - sofast = Sofast(measurement, camera, display) + sofast = ProcessSofastFringe(measurement, orientation, camera, display) # Update parameters sofast.params.mask_hist_thresh = params['mask_hist_thresh'] diff --git a/opencsp/app/sofast/test/test_integration_single_facet.py b/opencsp/app/sofast/test/test_integration_single_facet.py index e9720b96e..98ccd5367 100644 --- a/opencsp/app/sofast/test/test_integration_single_facet.py +++ b/opencsp/app/sofast/test/test_integration_single_facet.py @@ -7,11 +7,11 @@ import numpy as np -from opencsp.app.sofast.lib.DisplayShape import DisplayShape as Display +from opencsp.app.sofast.lib.DisplayShape import DisplayShape from opencsp.app.sofast.lib.DefinitionFacet import DefinitionFacet from opencsp.app.sofast.lib.ImageCalibrationScaling import ImageCalibrationScaling -from opencsp.app.sofast.lib.MeasurementSofastFringe import MeasurementSofastFringe as Measurement -from opencsp.app.sofast.lib.ProcessSofastFringe import ProcessSofastFringe as Sofast +from opencsp.app.sofast.lib.MeasurementSofastFringe import MeasurementSofastFringe +from opencsp.app.sofast.lib.ProcessSofastFringe import ProcessSofastFringe from opencsp.app.sofast.lib.SpatialOrientation import SpatialOrientation from opencsp.common.lib.camera.Camera import Camera from opencsp.common.lib.deflectometry.Surface2DPlano import Surface2DPlano @@ -45,7 +45,7 @@ def setUpClass(cls, base_dir: str | None = None): file_measurement = os.path.join(base_dir, 'measurement_facet.h5') # Load components - measurement = Measurement.load_from_hdf(file_measurement) + measurement = MeasurementSofastFringe.load_from_hdf(file_measurement) # Initialize data containers cls.slopes = [] @@ -58,7 +58,7 @@ def setUpClass(cls, base_dir: str | None = None): camera = Camera.load_from_hdf(file_dataset) orientation = SpatialOrientation.load_from_hdf(file_dataset) calibration = ImageCalibrationScaling.load_from_hdf(file_dataset) - display = Display.load_from_hdf(file_dataset) + display = DisplayShape.load_from_hdf(file_dataset) # Calibrate measurement measurement.calibrate_fringe_images(calibration) @@ -113,7 +113,7 @@ def setUpClass(cls, base_dir: str | None = None): params = load_hdf5_datasets(datasets, file_dataset) # Instantiate sofast object - sofast = Sofast(measurement, orientation, camera, display) + sofast = ProcessSofastFringe(measurement, orientation, camera, display) # Update parameters sofast.params.mask_hist_thresh = params['mask_hist_thresh'] diff --git a/opencsp/test/data/measurements_sofast_fringe/calculations_facet_ensemble/data.h5 b/opencsp/test/data/measurements_sofast_fringe/calculations_facet_ensemble/data.h5 index f2e1e88e182fa141ec5cae9b0ca432b1c98665b7..8248a8a8b1ffc6aeac89b39b8b3ac43c143e17aa 100644 GIT binary patch delta 636 zcmYk(Pe_w-9LMoJ&(dw_a&EdkSzAwPW@>B8@=xXVOiQh2ln{cQWXKL9Q&|$xwOE}> zd7$T3*rgyCdnj5&RA`{rphKsE4t5Ayy|+CFfAE6umoLBX;rl5*%!%1tPF!dg zgVJJ0Mt_lN${2;Tpvhld&t8f_t5B_duX@*V-BNk?-SKiEn0>LX(g)n}MPpo(t)7L=9@;#4gmqg?hN*K?54$g%1*TqX~Pk7k&h=4>Isuew@HboWcMih(bXOgBZeToWU?gFp9G{ zmsf1+-S@(R{&`L9^957t*QZ%sl1nO!>xoY%|cEA6r+m1KONlc0@dti%W*_ zPtXdTu296x#Eis}o}5ZtnY=ocPA1DMu5If}r8d?#TC*)U{n8J0)=~7Cd-d?2Mvqgp z*v)-!OVRo@`J3IEX^0z!Q_#=jIh6-EZ-%EoJmj7N3#a5FW(d?5q iUwZm)Ex-Qtc;e%$dgb|}wtJsypGNP9AH34)dh|c>KC!+4 delta 366 zcmWm3xlRI606@{q!*Os0To4yT1Q8JtTv!ItrvfUk@pFu=1Udq-n3B@YOhV#V*<)km zCnVt?h?nA2=iRo&xND1cT#P-PM8o+QD!1r;ij+&!U;p&J6?-$Et;c%7|00d1G&Bgf zFa;gcn4B9kn8h6Cv4BN*;DrxMSjGxg;l~;RSjPr7v4tSE5yB3_*hK_U>>-AI#BqQG z4w1wWQaDB$8Jyr0XE;X|Ipk4*i6Smg!X>UCag7_?qKpcvxWhdjP;;cATHo4}_4BK8 NR&}IIM-J3i=^s1_WGnyx From ba17214965942c29f95726ae1dc57561bf23685d Mon Sep 17 00:00:00 2001 From: Braden Date: Wed, 20 Mar 2024 22:10:53 -0600 Subject: [PATCH 17/36] Updated test-spatial-processing and test-slopeSolver to use SpatialOrientation object --- .../sofast/test/test_spatial_processing.py | 20 +++++++++---------- .../deflectometry/test/test_SlopeSolver.py | 8 +++----- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/opencsp/app/sofast/test/test_spatial_processing.py b/opencsp/app/sofast/test/test_spatial_processing.py index 23ba9115b..13fc8a9b4 100644 --- a/opencsp/app/sofast/test/test_spatial_processing.py +++ b/opencsp/app/sofast/test/test_spatial_processing.py @@ -7,9 +7,9 @@ import numpy as np from scipy.spatial.transform import Rotation -from opencsp.app.sofast.lib.DisplayShape import DisplayShape as Display -from opencsp.app.sofast.lib.MeasurementSofastFringe import MeasurementSofastFringe as Measurement +from opencsp.app.sofast.lib.MeasurementSofastFringe import MeasurementSofastFringe import opencsp.app.sofast.lib.spatial_processing as sp +from opencsp.app.sofast.lib.SpatialOrientation import SpatialOrientation from opencsp.common.lib.camera.Camera import Camera from opencsp.common.lib.geometry.Vxy import Vxy from opencsp.common.lib.geometry.Vxyz import Vxyz @@ -27,7 +27,7 @@ def setUpClass(cls): cls.data_file_facet = os.path.join(base_dir, 'calculations_facet/data.h5') cls.data_file_measurement = os.path.join(base_dir, 'measurement_facet.h5') - cls.display = Display.load_from_hdf(cls.data_file_facet) + cls.orientation = SpatialOrientation.load_from_hdf(cls.data_file_facet) cls.camera = Camera.load_from_hdf(cls.data_file_facet) def test_t_from_distance(self): @@ -38,14 +38,14 @@ def test_t_from_distance(self): # Load test data data = load_hdf5_datasets(datasets, self.data_file_facet) - measurement = Measurement.load_from_hdf(self.data_file_measurement) + measurement = MeasurementSofastFringe.load_from_hdf(self.data_file_measurement) # Perform calculation v_cam_optic_cam_exp = sp.t_from_distance( Vxy(data['v_mask_centroid_image']), measurement.optic_screen_dist, self.camera, - self.display.v_cam_screen_cam, + self.orientation.v_cam_screen_cam, ).data.squeeze() # Test @@ -62,7 +62,7 @@ def test_r_from_position(self): # Perform calculation r_optic_cam_exp = ( - sp.r_from_position(Vxyz(data['v_cam_optic_cam_exp']), self.display.v_cam_screen_cam).inv().as_rotvec() + sp.r_from_position(Vxyz(data['v_cam_optic_cam_exp']), self.orientation.v_cam_screen_cam).inv().as_rotvec() ) # Test @@ -100,11 +100,11 @@ def test_distance_error(self): # Load test data data = load_hdf5_datasets(datasets, self.data_file_facet) - measurement = Measurement.load_from_hdf(self.data_file_measurement) + measurement = MeasurementSofastFringe.load_from_hdf(self.data_file_measurement) # Perform calculation error_optic_screen_dist_2 = sp.distance_error( - self.display.v_cam_screen_cam, Vxyz(data['v_cam_optic_cam_refine_2']), measurement.optic_screen_dist + self.orientation.v_cam_screen_cam, Vxyz(data['v_cam_optic_cam_refine_2']), measurement.optic_screen_dist ) # Test @@ -143,7 +143,7 @@ def test_refine_v_distance(self): # Load test data data = load_hdf5_datasets(datasets, self.data_file_facet) - measurement = Measurement.load_from_hdf(self.data_file_measurement) + measurement = MeasurementSofastFringe.load_from_hdf(self.data_file_measurement) # Perform calculation r_cam_optic = Rotation.from_rotvec(data['r_optic_cam_refine_1']) @@ -151,7 +151,7 @@ def test_refine_v_distance(self): v_cam_optic_cam_refine_2 = sp.refine_v_distance( Vxyz(data['v_cam_optic_cam_refine_1']), measurement.optic_screen_dist, - self.display.v_cam_screen_cam, + self.orientation.v_cam_screen_cam, v_meas_pt_optic_cam, ).data.squeeze() diff --git a/opencsp/common/lib/deflectometry/test/test_SlopeSolver.py b/opencsp/common/lib/deflectometry/test/test_SlopeSolver.py index e7d5f72c5..42420001f 100644 --- a/opencsp/common/lib/deflectometry/test/test_SlopeSolver.py +++ b/opencsp/common/lib/deflectometry/test/test_SlopeSolver.py @@ -7,8 +7,7 @@ import numpy as np from scipy.spatial.transform import Rotation -from opencsp.app.sofast.lib.DisplayShape import DisplayShape as Display -from opencsp.app.sofast.lib.MeasurementSofastFringe import MeasurementSofastFringe as Measurement +from opencsp.app.sofast.lib.MeasurementSofastFringe import MeasurementSofastFringe from opencsp.app.sofast.lib.SpatialOrientation import SpatialOrientation from opencsp.common.lib.deflectometry.SlopeSolver import SlopeSolver from opencsp.common.lib.deflectometry.Surface2DParabolic import Surface2DParabolic @@ -42,13 +41,12 @@ def setUpClass(cls): ] # Load data data = load_hdf5_datasets(datasets, cls.data_file_facet) - display = Display.load_from_hdf(cls.data_file_facet) - measurement = Measurement.load_from_hdf(data_file_measurement) + measurement = MeasurementSofastFringe.load_from_hdf(data_file_measurement) + ori = SpatialOrientation.load_from_hdf(cls.data_file_facet) # Create spatial orientation object r_cam_optic = Rotation.from_rotvec(data['r_optic_cam_refine_1']).inv() v_cam_optic_cam = Vxyz(data['v_cam_optic_cam_refine_2']) - ori = SpatialOrientation(display.r_cam_screen, display.v_cam_screen_cam) ori.orient_optic_cam(r_cam_optic, v_cam_optic_cam) # Perform calculations From e517170190929025628351dbf9734bf26b721866 Mon Sep 17 00:00:00 2001 From: Braden Date: Thu, 21 Mar 2024 12:09:52 -0600 Subject: [PATCH 18/36] Renamed sofast fringe example to spatial-orientation --- ..._camera_pose.py => example_calibration_spatial_orientation.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename example/sofast_fringe/{example_calibration_camera_pose.py => example_calibration_spatial_orientation.py} (100%) diff --git a/example/sofast_fringe/example_calibration_camera_pose.py b/example/sofast_fringe/example_calibration_spatial_orientation.py similarity index 100% rename from example/sofast_fringe/example_calibration_camera_pose.py rename to example/sofast_fringe/example_calibration_spatial_orientation.py From 088caedc15f4a47315c59d164eeb057fc55d20e4 Mon Sep 17 00:00:00 2001 From: Braden Date: Thu, 21 Mar 2024 13:09:43 -0600 Subject: [PATCH 19/36] Added HDF5 IO Abstract class inheritance to SpatialOrientation --- opencsp/app/sofast/lib/SpatialOrientation.py | 52 +++++++++++++------- 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/opencsp/app/sofast/lib/SpatialOrientation.py b/opencsp/app/sofast/lib/SpatialOrientation.py index 0ec5ef3ed..f426a571b 100644 --- a/opencsp/app/sofast/lib/SpatialOrientation.py +++ b/opencsp/app/sofast/lib/SpatialOrientation.py @@ -2,12 +2,10 @@ from opencsp.common.lib.geometry.TransformXYZ import TransformXYZ from opencsp.common.lib.geometry.Vxyz import Vxyz -from opencsp.common.lib.tool import hdf5_tools +import opencsp.common.lib.tool.hdf5_tools as ht -# TODO: Add HDF5 abstract class inheritance - -class SpatialOrientation: +class SpatialOrientation(ht.HDF5_IO_Abstract): """Holds relative orientations of camera, screen, and optic for deflectometry systems""" def __init__(self, r_cam_screen: Rotation, v_cam_screen_cam: Vxyz) -> 'SpatialOrientation': @@ -141,13 +139,12 @@ def save_to_hdf(self, file: str, prefix: str = '') -> None: HDF file to save to prefix : str Prefix to append to folder path within HDF file (folders must be separated by "/") - """ datasets = [prefix + 'SpatialOrientation/r_cam_screen', prefix + 'SpatialOrientation/v_cam_screen_cam'] data = [self.r_cam_screen.as_rotvec(), self.v_cam_screen_cam.data] - hdf5_tools.save_hdf5_datasets(data, datasets, file) + ht.save_hdf5_datasets(data, datasets, file) def save_all_to_hdf(self, file: str, prefix: str = '') -> None: """Saves all data to HDF file. Data is stored as prefix + SpatialOrientation/... @@ -174,27 +171,46 @@ def save_all_to_hdf(self, file: str, prefix: str = '') -> None: self.v_cam_optic_cam.data, ] - hdf5_tools.save_hdf5_datasets(data, datasets, file) + ht.save_hdf5_datasets(data, datasets, file) @classmethod - def load_from_hdf(cls, file: str) -> 'SpatialOrientation': - """Loads camera-screen orientation data from HDF file""" - datasets = ['SpatialOrientation/r_cam_screen', 'SpatialOrientation/v_cam_screen_cam'] - data = hdf5_tools.load_hdf5_datasets(datasets, file) + def load_from_hdf(cls, file: str, prefix: str = '') -> 'SpatialOrientation': + """Loads data from given file. Assumes data is stored as: PREFIX + SpatialOrientation/... + + Parameters + ---------- + file : str + HDF file to save to + prefix : str + Prefix to append to folder path within HDF file (folders must be separated by "/") + """ + datasets = [ + prefix + 'SpatialOrientation/r_cam_screen', + prefix + 'SpatialOrientation/v_cam_screen_cam', + ] + data = ht.load_hdf5_datasets(datasets, file) r_cam_screen = Rotation.from_rotvec(data['r_cam_screen']) v_cam_screen_cam = Vxyz(data['v_cam_screen_cam']) return cls(r_cam_screen, v_cam_screen_cam) @classmethod - def load_all_from_hdf(cls, file: str) -> 'SpatialOrientation': - """Loads all data from HDF file""" + def load_all_from_hdf(cls, file: str, prefix: str = '') -> 'SpatialOrientation': + """Loads data from given file. Assumes data is stored as: PREFIX + SpatialOrientation/... + + Parameters + ---------- + file : str + HDF file to save to + prefix : str + Prefix to append to folder path within HDF file (folders must be separated by "/") + """ datasets = [ - 'SpatialOrientation/r_cam_screen', - 'SpatialOrientation/v_cam_screen_cam', - 'SpatialOrientation/r_cam_optic', - 'SpatialOrientation/v_cam_optic_cam', + prefix + 'SpatialOrientation/r_cam_screen', + prefix + 'SpatialOrientation/v_cam_screen_cam', + prefix + 'SpatialOrientation/r_cam_optic', + prefix + 'SpatialOrientation/v_cam_optic_cam', ] - data = hdf5_tools.load_hdf5_datasets(datasets, file) + data = ht.load_hdf5_datasets(datasets, file) r_cam_screen = Rotation.from_rotvec(data['r_cam_screen']) v_cam_screen_cam = Vxyz(data['v_cam_screen_cam']) From 645b5a7b52f687b02e8b6932d1ea3dbaafb0f07b Mon Sep 17 00:00:00 2001 From: Braden Date: Thu, 21 Mar 2024 14:35:22 -0600 Subject: [PATCH 20/36] Edited spatial orientation HDF IO. --- opencsp/app/sofast/lib/SpatialOrientation.py | 92 ++++++++------------ 1 file changed, 35 insertions(+), 57 deletions(-) diff --git a/opencsp/app/sofast/lib/SpatialOrientation.py b/opencsp/app/sofast/lib/SpatialOrientation.py index f426a571b..0eb09de5c 100644 --- a/opencsp/app/sofast/lib/SpatialOrientation.py +++ b/opencsp/app/sofast/lib/SpatialOrientation.py @@ -140,36 +140,28 @@ def save_to_hdf(self, file: str, prefix: str = '') -> None: prefix : str Prefix to append to folder path within HDF file (folders must be separated by "/") """ - datasets = [prefix + 'SpatialOrientation/r_cam_screen', prefix + 'SpatialOrientation/v_cam_screen_cam'] - - data = [self.r_cam_screen.as_rotvec(), self.v_cam_screen_cam.data] - - ht.save_hdf5_datasets(data, datasets, file) - - def save_all_to_hdf(self, file: str, prefix: str = '') -> None: - """Saves all data to HDF file. Data is stored as prefix + SpatialOrientation/... - - Parameters - ---------- - file : str - HDF file to save to - prefix : str - Prefix to append to folder path within HDF file (folders must be separated by "/") - - """ - datasets = [ - prefix + 'SpatialOrientation/r_cam_screen', - prefix + 'SpatialOrientation/v_cam_screen_cam', - prefix + 'SpatialOrientation/r_cam_optic', - prefix + 'SpatialOrientation/v_cam_optic_cam', - ] - - data = [ - self.r_cam_screen.as_rotvec(), - self.v_cam_screen_cam.data, - self.r_cam_optic.as_rotvec(), - self.v_cam_optic_cam.data, - ] + if self.optic_oriented: + datasets = [ + prefix + 'SpatialOrientation/r_cam_screen', + prefix + 'SpatialOrientation/v_cam_screen_cam', + prefix + 'SpatialOrientation/optic_oriented', + prefix + 'SpatialOrientation/r_cam_optic', + prefix + 'SpatialOrientation/v_cam_optic_cam', + ] + data = [ + self.r_cam_screen.as_rotvec(), + self.v_cam_screen_cam.data, + self.optic_oriented, + self.r_cam_optic.as_rotvec(), + self.v_cam_optic_cam.data, + ] + else: + datasets = [ + prefix + 'SpatialOrientation/r_cam_screen', + prefix + 'SpatialOrientation/v_cam_screen_cam', + prefix + 'SpatialOrientation/optic_oriented', + ] + data = [self.r_cam_screen.as_rotvec(), self.v_cam_screen_cam.data, self.optic_oriented] ht.save_hdf5_datasets(data, datasets, file) @@ -184,40 +176,26 @@ def load_from_hdf(cls, file: str, prefix: str = '') -> 'SpatialOrientation': prefix : str Prefix to append to folder path within HDF file (folders must be separated by "/") """ + # Load screen-camera orientation information datasets = [ prefix + 'SpatialOrientation/r_cam_screen', prefix + 'SpatialOrientation/v_cam_screen_cam', + prefix + 'SpatialOrientation/optic_oriented', ] data = ht.load_hdf5_datasets(datasets, file) r_cam_screen = Rotation.from_rotvec(data['r_cam_screen']) v_cam_screen_cam = Vxyz(data['v_cam_screen_cam']) - return cls(r_cam_screen, v_cam_screen_cam) - - @classmethod - def load_all_from_hdf(cls, file: str, prefix: str = '') -> 'SpatialOrientation': - """Loads data from given file. Assumes data is stored as: PREFIX + SpatialOrientation/... - - Parameters - ---------- - file : str - HDF file to save to - prefix : str - Prefix to append to folder path within HDF file (folders must be separated by "/") - """ - datasets = [ - prefix + 'SpatialOrientation/r_cam_screen', - prefix + 'SpatialOrientation/v_cam_screen_cam', - prefix + 'SpatialOrientation/r_cam_optic', - prefix + 'SpatialOrientation/v_cam_optic_cam', - ] - data = ht.load_hdf5_datasets(datasets, file) - - r_cam_screen = Rotation.from_rotvec(data['r_cam_screen']) - v_cam_screen_cam = Vxyz(data['v_cam_screen_cam']) - r_cam_optic = Rotation.from_rotvec(data['r_cam_optic']) - v_cam_optic_cam = Vxyz(data['v_cam_optic_cam']) - ori = cls(r_cam_screen, v_cam_screen_cam) - ori.orient_optic_cam(r_cam_optic, v_cam_optic_cam) + + # If optic is oriented, load optic orientation information + if data['optic_oriented']: + datasets = [ + prefix + 'SpatialOrientation/r_cam_optic', + prefix + 'SpatialOrientation/v_cam_optic_cam', + ] + data = ht.load_hdf5_datasets(datasets, file) + r_cam_optic = Rotation.from_rotvec(data['r_cam_optic']) + v_cam_optic_cam = Vxyz(data['v_cam_optic_cam']) + ori.orient_optic_cam(r_cam_optic, v_cam_optic_cam) return ori From 189abe9e93bb9a66a6decf4903e7800d21cfd714 Mon Sep 17 00:00:00 2001 From: Braden Date: Thu, 21 Mar 2024 14:35:32 -0600 Subject: [PATCH 21/36] Updated SO unit test --- .../sofast/test/test_SpatialOrientation.py | 28 +++++++++++++++++-- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/opencsp/app/sofast/test/test_SpatialOrientation.py b/opencsp/app/sofast/test/test_SpatialOrientation.py index a735d9d34..d5165e32f 100644 --- a/opencsp/app/sofast/test/test_SpatialOrientation.py +++ b/opencsp/app/sofast/test/test_SpatialOrientation.py @@ -1,6 +1,6 @@ """Unit test suite to test the SpatialOrientation class""" -import os +from os.path import join, dirname import unittest import numpy as np @@ -16,10 +16,10 @@ class TestSpatialOrientation(unittest.TestCase): @classmethod def setUpClass(cls): # Get test data location - base_dir = os.path.join(opencsp_code_dir(), 'test/data/measurements_sofast_fringe') + base_dir = join(opencsp_code_dir(), 'test/data/measurements_sofast_fringe') # Define test data files for single facet processing - data_file_facet = os.path.join(base_dir, 'calculations_facet/data.h5') + data_file_facet = join(base_dir, 'calculations_facet/data.h5') # Load data datasets = [ @@ -66,6 +66,28 @@ def test_rotation_ring_2(self): I_calc = self.so.r_cam_screen * self.so.r_optic_cam * self.so.r_screen_optic np.testing.assert_allclose(I_exp, I_calc.as_matrix(), atol=1e-9, rtol=0) + def test_io_oriented_optic(self): + file = join(dirname(__file__), 'data/output/test_spatial_orientation_oriented_optic.h5') + # Save + self.so.save_to_hdf(file) + # Load + ori = SpatialOrientation.load_from_hdf(file) + # Check optic is oriented + np.testing.assert_equal(ori.optic_oriented, True) + + def test_io_unoriented_optic(self): + file = join(dirname(__file__), 'data/output/test_spatial_orientation_unoriented_optic.h5') + # Save + r_cam_screen = self.so.r_cam_screen + v_cam_screen_cam = self.so.v_cam_screen_cam + ori_1 = SpatialOrientation(r_cam_screen, v_cam_screen_cam) + ori_1.save_to_hdf(file) + # Load + ori_2 = SpatialOrientation.load_from_hdf(file) + # Check optic not oriented + np.testing.assert_equal(ori_1.optic_oriented, False) + np.testing.assert_equal(ori_2.optic_oriented, False) + if __name__ == '__main__': unittest.main() From 5a446da15cc8c6f03e2da1e2c6eb164ab353f6ba Mon Sep 17 00:00:00 2001 From: Braden Date: Thu, 21 Mar 2024 14:35:41 -0600 Subject: [PATCH 22/36] Updated unit test data --- .../calculations_facet/data.h5 | Bin 1592784 -> 1594960 bytes .../calculations_facet/data_distorted_3d.h5 | Bin 1594720 -> 1596896 bytes .../calculations_facet/data_no_ls.h5 | Bin 1592784 -> 1594960 bytes .../calculations_facet/data_plano.h5 | Bin 1592416 -> 1594592 bytes .../calculations_facet/data_rectangular.h5 | Bin 1585056 -> 1587232 bytes .../calculations_facet_ensemble/data.h5 | Bin 4986926 -> 4989102 bytes .../calculations_undefined_mirror/data.h5 | Bin 1079834 -> 1082010 bytes .../spatial_orientation.h5 | Bin 4752 -> 6928 bytes 8 files changed, 0 insertions(+), 0 deletions(-) diff --git a/opencsp/test/data/measurements_sofast_fringe/calculations_facet/data.h5 b/opencsp/test/data/measurements_sofast_fringe/calculations_facet/data.h5 index 19344c8d39d48328f42d8f9510d0b13603a033da..f39e5f9a028c5c694d3d6283d24012b53883ea20 100644 GIT binary patch delta 319 zcmcaGJMqGr#0eTq0bz|=ty+w&T1>54%&l51ty-+DT5PRa?5$cHty-L|T3oGK+^t$X zty;XTT70cq{HUP)@obUS$oqv-)264R#hRS9r0UkH_$xKVbpgFNTjS_OvF^oUf6qTmhP9@Rq+Xu gBKED@ukFQnWw^0(bABf-|3UP)@o?%2Jj>*n>l6K83<^go41+qac0{(-|s3*na6$se54%&l51ty-+DT5PRa?5$cHty-L|T3oGK+^t$X zty;XTT70cq{HUP)@obUS$oqv-)264R#hRS9r0UkH_$xKVbpgFNT& delta 133 zcmV~$tqy`v0EXcm@Lv)9KQ`jo8=QAwMBUuXHZH~RRi|TvU^AEu7DqMO#5?kNsz+K~ z)3fuc-4S7gCwx&8b diff --git a/opencsp/test/data/measurements_sofast_fringe/calculations_facet/data_plano.h5 b/opencsp/test/data/measurements_sofast_fringe/calculations_facet/data_plano.h5 index 4aa7972f6bbf5b42da7e8d39752f13a89b26d0bf..30ad264fca3ded0dbf2ee08b41a60cd1afe1862a 100644 GIT binary patch delta 206 zcmaDbBk{q?#0eTq4?-HXTD2HkwU}D9m|L}2TD4ePwb)v<*ju$YTD3S^wYXZfxLdV& zTD5pvwfI`K_*=CETD1gQwS-!=gj=;lTD3%5wZvMr#J6fmn7K{&b77&Ti%h^d%~`5U`&X5}+yx5YO~ gizKwZa`tY5^0~JQbNi$||HX;lUE@=^KG7=~eE{7KTre2p64MR05$!Im!O5PAa6Dw-jSks{q4o?R*UZ4z8`br83L zM`915ql-|e-pf5**?Ext4i?Y(Rx}4;5P8BR3Ph185oN+6Y{DTPh)2RDDuhQU;)(Ey zDp4cqL_jnSkk3&6cw*^YC{ZPy&XQ3-{Yc`;EFSBQk~iIgtXwnqn!A;CWw~#Dsh)j2 jK|JGlY0J*s4%d#Qqm%Nr^R1NLTuhs7*rUp|?`@f8xz2x-MtkQ2NdWC^E35kN{v7jA(PI4Az9*jCq& z#KQ&QL?XQCh_2{~zDUJD48=%{MJ6U1%J%{{eqFB-Q`` diff --git a/opencsp/test/data/measurements_sofast_fringe/calculations_facet_ensemble/data.h5 b/opencsp/test/data/measurements_sofast_fringe/calculations_facet_ensemble/data.h5 index 8248a8a8b1ffc6aeac89b39b8b3ac43c143e17aa..3d120ceec9d3267034f8aaa929232bc16dd3ce87 100644 GIT binary patch delta 351 zcmWN~yH1l~0D$4NMW_gfe<>CyQasid+KS3i<)EnO|HpU*Iw(UIXb5pI(Zs}eagsFg zy93>tGQdc}C9se%ySTtL;Bx~{tKCkw4%_LU<#em`4M|B_GSVRzq*J;iD;K3(E=iB{ zN}pVoez_u%RLRx}QrTe3g`9zRf!F+L z1vty+w&T1>54%&l51ty-+DT5PRa?5$cHty-L|T3oGK+^t$X zty;XTT70cq{9CmIJ`^)LOqVVZu;Bs$29tpFMlC;9Mu*9jtoD-m1tpou@%crWsd*)-DGUsZ3=kl(`66pB6Av?k z0aRdu)WnT)n;keL7&kX?3UF>dzo&3Zj4#lkW-2h)oazbKq`)@Bo;q8{q%| delta 52 zcmbPWHbHfQ2Gazgjaq)Jj0Te{S?xI)86ZGo;zs$+4_M=v7@0Pwb4W5yJfN|;fm47J E09+#t9{>OV From 16efbfba112b7e6f79231f713762b8bcc4d30bd8 Mon Sep 17 00:00:00 2001 From: Braden Date: Thu, 21 Mar 2024 14:51:37 -0600 Subject: [PATCH 23/36] Added the creation of save directories if needed in unit tests --- opencsp/app/sofast/test/test_CalibrateDisplayShape.py | 1 + opencsp/app/sofast/test/test_DisplayShape.py | 11 ++++++++--- opencsp/app/sofast/test/test_SpatialOrientation.py | 9 +++++++-- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/opencsp/app/sofast/test/test_CalibrateDisplayShape.py b/opencsp/app/sofast/test/test_CalibrateDisplayShape.py index 60a630eea..0d48f440b 100644 --- a/opencsp/app/sofast/test/test_CalibrateDisplayShape.py +++ b/opencsp/app/sofast/test/test_CalibrateDisplayShape.py @@ -29,6 +29,7 @@ def setUpClass(cls): dir_input_sofast = join(opencsp_code_dir(), 'app/sofast/test/data/data_measurement') dir_input_def = join(opencsp_code_dir(), 'common/lib/deflectometry/test/data/data_measurement') dir_output = join(opencsp_code_dir(), 'app/sofast/test/data/data_expected') + ft.create_directories_if_necessary(dir_output) # Define input files resolution_xy = [100, 100] # sample density of screen diff --git a/opencsp/app/sofast/test/test_DisplayShape.py b/opencsp/app/sofast/test/test_DisplayShape.py index 738b54d1c..a965b2e10 100644 --- a/opencsp/app/sofast/test/test_DisplayShape.py +++ b/opencsp/app/sofast/test/test_DisplayShape.py @@ -9,6 +9,7 @@ from opencsp.app.sofast.lib.DisplayShape import DisplayShape from opencsp.common.lib.geometry.Vxy import Vxy from opencsp.common.lib.geometry.Vxyz import Vxyz +import opencsp.common.lib.tool.file_tools as ft class TestDisplayShape(unittest.TestCase): @@ -59,6 +60,10 @@ def setUpClass(cls): ) ) + # Set up save path + cls.save_dir = join(dirname(__file__), 'data/output') + ft.create_directories_if_necessary(cls.save_dir) + def test_rectangular2D(self): # Instantiate display object name = 'Test DisplayShape' @@ -96,7 +101,7 @@ def test_save_load_hdf_dist_3d(self): # Instantiate display object name = 'Test DisplayShape' disp = DisplayShape(self.grid_data_3D, name) - file = join(dirname(__file__), 'data/output/test_display_shape_dist_3d.h5') + file = join(self.save_dir, 'test_display_shape_dist_3d.h5') # Save disp.save_to_hdf(file) @@ -108,7 +113,7 @@ def test_save_load_hdf_dist_2d(self): # Instantiate display object name = 'Test DisplayShape' disp = DisplayShape(self.grid_data_2D, name) - file = join(dirname(__file__), 'data/output/test_display_shape_dist_2d.h5') + file = join(self.save_dir, 'test_display_shape_dist_2d.h5') # Save disp.save_to_hdf(file) @@ -120,7 +125,7 @@ def test_save_load_hdf_rectangular(self): # Instantiate display object name = 'Test DisplayShape' disp = DisplayShape(self.grid_data_rect2D, name) - file = join(dirname(__file__), 'data/output/test_display_shape_rect.h5') + file = join(self.save_dir, 'test_display_shape_rect.h5') # Save disp.save_to_hdf(file) diff --git a/opencsp/app/sofast/test/test_SpatialOrientation.py b/opencsp/app/sofast/test/test_SpatialOrientation.py index d5165e32f..c70c713fc 100644 --- a/opencsp/app/sofast/test/test_SpatialOrientation.py +++ b/opencsp/app/sofast/test/test_SpatialOrientation.py @@ -10,6 +10,7 @@ from opencsp.common.lib.geometry.Vxyz import Vxyz from opencsp.common.lib.opencsp_path.opencsp_root_path import opencsp_code_dir from opencsp.common.lib.tool.hdf5_tools import load_hdf5_datasets +import opencsp.common.lib.tool.file_tools as ft class TestSpatialOrientation(unittest.TestCase): @@ -38,6 +39,10 @@ def setUpClass(cls): # Save spatial orientation cls.so = ori + # Set up save path + cls.save_dir = join(dirname(__file__), 'data/output') + ft.create_directories_if_necessary(cls.save_dir) + def test_translation_ring_1(self): v_exp = np.zeros(3) v_calc = ( @@ -67,7 +72,7 @@ def test_rotation_ring_2(self): np.testing.assert_allclose(I_exp, I_calc.as_matrix(), atol=1e-9, rtol=0) def test_io_oriented_optic(self): - file = join(dirname(__file__), 'data/output/test_spatial_orientation_oriented_optic.h5') + file = join(self.save_dir, 'test_spatial_orientation_oriented_optic.h5') # Save self.so.save_to_hdf(file) # Load @@ -76,7 +81,7 @@ def test_io_oriented_optic(self): np.testing.assert_equal(ori.optic_oriented, True) def test_io_unoriented_optic(self): - file = join(dirname(__file__), 'data/output/test_spatial_orientation_unoriented_optic.h5') + file = join(self.save_dir, 'test_spatial_orientation_unoriented_optic.h5') # Save r_cam_screen = self.so.r_cam_screen v_cam_screen_cam = self.so.v_cam_screen_cam From 7856e296b9522777e3d1d9a95b8deff572ca67b7 Mon Sep 17 00:00:00 2001 From: Braden Date: Thu, 21 Mar 2024 15:05:18 -0600 Subject: [PATCH 24/36] Fixed unit tests, made save dirs when necessary, sorted glob lists. --- .../app/sofast/test/test_CalibrateDisplayShape.py | 13 ++++++++----- opencsp/app/sofast/test/test_SpatialOrientation.py | 4 ++-- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/opencsp/app/sofast/test/test_CalibrateDisplayShape.py b/opencsp/app/sofast/test/test_CalibrateDisplayShape.py index 0d48f440b..e8e979a98 100644 --- a/opencsp/app/sofast/test/test_CalibrateDisplayShape.py +++ b/opencsp/app/sofast/test/test_CalibrateDisplayShape.py @@ -2,19 +2,19 @@ """ import os -from os.path import join, dirname +from os.path import join import unittest from glob import glob import numpy as np import pytest -from opencsp.common.lib.opencsp_path.opencsp_root_path import opencsp_code_dir from opencsp.app.sofast.lib.CalibrateDisplayShape import CalibrateDisplayShape, DataInput from opencsp.app.sofast.lib.MeasurementSofastFringe import MeasurementSofastFringe from opencsp.common.lib.camera.Camera import Camera from opencsp.common.lib.deflectometry.ImageProjection import ImageProjection from opencsp.common.lib.geometry.Vxyz import Vxyz +from opencsp.common.lib.opencsp_path.opencsp_root_path import opencsp_code_dir from opencsp.common.lib.tool.hdf5_tools import load_hdf5_datasets import opencsp.common.lib.tool.file_tools as ft import opencsp.common.lib.tool.log_tools as lt @@ -29,7 +29,6 @@ def setUpClass(cls): dir_input_sofast = join(opencsp_code_dir(), 'app/sofast/test/data/data_measurement') dir_input_def = join(opencsp_code_dir(), 'common/lib/deflectometry/test/data/data_measurement') dir_output = join(opencsp_code_dir(), 'app/sofast/test/data/data_expected') - ft.create_directories_if_necessary(dir_output) # Define input files resolution_xy = [100, 100] # sample density of screen @@ -38,6 +37,7 @@ def setUpClass(cls): file_camera_distortion = join(dir_input_sofast, 'camera_screen_shape.h5') file_image_projection = join(dir_input_sofast, 'image_projection.h5') files_screen_shape_measurement = glob(join(dir_input_sofast, 'screen_shape_sofast_measurements/pose_*.h5')) + files_screen_shape_measurement.sort() # Load input data pts_marker_data = np.loadtxt(file_point_locations, delimiter=',', dtype=float, skiprows=1) @@ -68,6 +68,9 @@ def setUpClass(cls): ['pts_xy_screen_fraction', 'pts_xyz_screen_coords'], join(dir_output, 'screen_distortion_data_100_100.h5') ) + cls.save_dir_local = join(opencsp_code_dir(), 'app/sofast/test/data/output') + ft.create_directories_if_necessary(cls.save_dir_local) + @pytest.mark.skipif(os.name != 'nt', reason='Does not pass in Linux environment for unkonwn reason.') def test_xy_screen_fraction(self): """Tests xy points""" @@ -87,13 +90,13 @@ def test_xyz_screen_coords(self): def test_save_display_object(self): """Tests saving DisplayShape object""" display_shape = self.cal.as_DisplayShape('Test display') - file = join(dirname(__file__), 'data/output/test_calibration_display.h5') + file = join(self.save_dir_local, 'test_calibration_display.h5') display_shape.save_to_hdf(file) if __name__ == '__main__': # Set up save dir - save_dir = join(dirname(__file__), 'data/output') + save_dir = join(opencsp_code_dir(), 'app/sofast/test/data/output') ft.create_directories_if_necessary(save_dir) # Set up logger diff --git a/opencsp/app/sofast/test/test_SpatialOrientation.py b/opencsp/app/sofast/test/test_SpatialOrientation.py index c70c713fc..97d1196ba 100644 --- a/opencsp/app/sofast/test/test_SpatialOrientation.py +++ b/opencsp/app/sofast/test/test_SpatialOrientation.py @@ -1,6 +1,6 @@ """Unit test suite to test the SpatialOrientation class""" -from os.path import join, dirname +from os.path import join import unittest import numpy as np @@ -40,7 +40,7 @@ def setUpClass(cls): cls.so = ori # Set up save path - cls.save_dir = join(dirname(__file__), 'data/output') + cls.save_dir = join(opencsp_code_dir(), 'app/sofast/test/data/output') ft.create_directories_if_necessary(cls.save_dir) def test_translation_ring_1(self): From 7389f32b764dbff646df35f8019c42689de338b4 Mon Sep 17 00:00:00 2001 From: Braden Date: Thu, 21 Mar 2024 15:41:44 -0600 Subject: [PATCH 25/36] Sorted all glob lists --- opencsp/app/scene_reconstruction/lib/SceneReconstruction.py | 1 + opencsp/app/sofast/test/test_integration_single_facet.py | 1 + 2 files changed, 2 insertions(+) diff --git a/opencsp/app/scene_reconstruction/lib/SceneReconstruction.py b/opencsp/app/scene_reconstruction/lib/SceneReconstruction.py index 3c9a9bcbf..5610e7dcc 100644 --- a/opencsp/app/scene_reconstruction/lib/SceneReconstruction.py +++ b/opencsp/app/scene_reconstruction/lib/SceneReconstruction.py @@ -54,6 +54,7 @@ def __init__(self, camera: Camera, known_point_locations: ndarray, image_filter_ self.camera = camera self.known_point_locations = known_point_locations self.image_paths = glob(image_filter_path) + self.image_paths.sort() # Declare attributes self.images: list[ImageMarker] # Loaded image marker objects diff --git a/opencsp/app/sofast/test/test_integration_single_facet.py b/opencsp/app/sofast/test/test_integration_single_facet.py index 98ccd5367..93a474b35 100644 --- a/opencsp/app/sofast/test/test_integration_single_facet.py +++ b/opencsp/app/sofast/test/test_integration_single_facet.py @@ -38,6 +38,7 @@ def setUpClass(cls, base_dir: str | None = None): # Find all test files cls.files_dataset = glob.glob(os.path.join(base_dir, 'calculations_facet/data*.h5')) + cls.files_dataset.sort() if len(cls.files_dataset) == 0: raise ValueError('No single-facet datsets found.') From ea3da165fd228ac9490f351f354520b5b826c9f1 Mon Sep 17 00:00:00 2001 From: Braden Date: Mon, 25 Mar 2024 09:48:45 -0600 Subject: [PATCH 26/36] Formatted four files to be Black compliant. --- opencsp/app/sofast/lib/DisplayShape.py | 22 +++++-------------- opencsp/app/sofast/lib/ProcessSofastFringe.py | 8 +++---- opencsp/app/sofast/lib/SpatialOrientation.py | 5 +---- 3 files changed, 9 insertions(+), 26 deletions(-) diff --git a/opencsp/app/sofast/lib/DisplayShape.py b/opencsp/app/sofast/lib/DisplayShape.py index 7ffdc9166..04cf0f671 100644 --- a/opencsp/app/sofast/lib/DisplayShape.py +++ b/opencsp/app/sofast/lib/DisplayShape.py @@ -177,33 +177,24 @@ def load_from_hdf(cls, file: str, prefix: str = '') -> 'DisplayShape': Prefix to append to folder path within HDF file (folders must be separated by "/") """ # Load grid data - datasets = [ - prefix + 'DisplayShape/screen_model', - prefix + 'DisplayShape/name', - ] + datasets = [prefix + 'DisplayShape/screen_model', prefix + 'DisplayShape/name'] data = ht.load_hdf5_datasets(datasets, file) # Rectangular if data['screen_model'] == 'rectangular2D': - datasets = ['DisplayShape/screen_x', 'DisplayShape/screen_y'] + datasets = [prefix + 'DisplayShape/screen_x', prefix + 'DisplayShape/screen_y'] grid_data = ht.load_hdf5_datasets(datasets, file) # Distorted 2D elif data['screen_model'] == 'distorted2D': - datasets = [ - 'DisplayShape/xy_screen_fraction', - 'DisplayShape/xy_screen_coords', - ] + datasets = [prefix + 'DisplayShape/xy_screen_fraction', prefix + 'DisplayShape/xy_screen_coords'] grid_data = ht.load_hdf5_datasets(datasets, file) grid_data['xy_screen_fraction'] = Vxy(grid_data['xy_screen_fraction']) grid_data['xy_screen_coords'] = Vxy(grid_data['xy_screen_coords']) # Distorted 3D elif data['screen_model'] == 'distorted3D': - datasets = [ - 'DisplayShape/xy_screen_fraction', - 'DisplayShape/xyz_screen_coords', - ] + datasets = [prefix + 'DisplayShape/xy_screen_fraction', prefix + 'DisplayShape/xyz_screen_coords'] grid_data = ht.load_hdf5_datasets(datasets, file) grid_data['xy_screen_fraction'] = Vxy(grid_data['xy_screen_fraction']) grid_data['xyz_screen_coords'] = Vxyz(grid_data['xyz_screen_coords']) @@ -213,10 +204,7 @@ def load_from_hdf(cls, file: str, prefix: str = '') -> 'DisplayShape': grid_data.update({'screen_model': data['screen_model']}) # Return display object - kwargs = { - 'name': data['name'], - 'grid_data': grid_data, - } + kwargs = {'name': data['name'], 'grid_data': grid_data} return cls(**kwargs) def save_to_hdf(self, file: str, prefix: str = '') -> None: diff --git a/opencsp/app/sofast/lib/ProcessSofastFringe.py b/opencsp/app/sofast/lib/ProcessSofastFringe.py index 6ad9eece6..56cac5c54 100644 --- a/opencsp/app/sofast/lib/ProcessSofastFringe.py +++ b/opencsp/app/sofast/lib/ProcessSofastFringe.py @@ -158,11 +158,9 @@ class ProcessSofastFringe(HDF5_SaveAbstract): - v_mask_centroid_image """ - def __init__(self, - measurement: MeasurementSofastFringe, - orientation: SpatialOrientation, - camera: Camera, - display: Display) -> 'ProcessSofastFringe': + def __init__( + self, measurement: MeasurementSofastFringe, orientation: SpatialOrientation, camera: Camera, display: Display + ) -> 'ProcessSofastFringe': """ SOFAST processing class. diff --git a/opencsp/app/sofast/lib/SpatialOrientation.py b/opencsp/app/sofast/lib/SpatialOrientation.py index 0eb09de5c..18801dd27 100644 --- a/opencsp/app/sofast/lib/SpatialOrientation.py +++ b/opencsp/app/sofast/lib/SpatialOrientation.py @@ -189,10 +189,7 @@ def load_from_hdf(cls, file: str, prefix: str = '') -> 'SpatialOrientation': # If optic is oriented, load optic orientation information if data['optic_oriented']: - datasets = [ - prefix + 'SpatialOrientation/r_cam_optic', - prefix + 'SpatialOrientation/v_cam_optic_cam', - ] + datasets = [prefix + 'SpatialOrientation/r_cam_optic', prefix + 'SpatialOrientation/v_cam_optic_cam'] data = ht.load_hdf5_datasets(datasets, file) r_cam_optic = Rotation.from_rotvec(data['r_cam_optic']) v_cam_optic_cam = Vxyz(data['v_cam_optic_cam']) From b248c0a75b39dbc0579d98a91e06d84b88f0d449 Mon Sep 17 00:00:00 2001 From: Braden Date: Mon, 25 Mar 2024 10:21:18 -0600 Subject: [PATCH 27/36] Added/removed sofast doc files to pass doc checks. --- .../example/camera_calibration/config.rst | 8 ++++++++ .../example/camera_calibration/index.rst | 11 +++++++++++ doc/source/example/index.rst | 1 + doc/source/example/sofast/config.rst | 8 -------- doc/source/example/sofast/index.rst | 6 +++--- .../app/sofast/calibration/config.rst | 19 ------------------- .../app/sofast/calibration/index.rst | 10 ---------- .../library_reference/app/sofast/config.rst | 12 +++++++++++- .../library_reference/app/sofast/index.rst | 3 +-- .../example_view_camera_distortion.py | 4 ++-- 10 files changed, 37 insertions(+), 45 deletions(-) create mode 100644 doc/source/example/camera_calibration/config.rst create mode 100644 doc/source/example/camera_calibration/index.rst delete mode 100644 doc/source/library_reference/app/sofast/calibration/config.rst delete mode 100644 doc/source/library_reference/app/sofast/calibration/index.rst diff --git a/doc/source/example/camera_calibration/config.rst b/doc/source/example/camera_calibration/config.rst new file mode 100644 index 000000000..0b529a1a8 --- /dev/null +++ b/doc/source/example/camera_calibration/config.rst @@ -0,0 +1,8 @@ +View Camera Distortion +====================== + +.. currentmodule:: example.camera_calibration.example_view_camera_distortion + +.. automodule:: example.camera_calibration.example_view_camera_distortion + :members: + :show-inheritance: diff --git a/doc/source/example/camera_calibration/index.rst b/doc/source/example/camera_calibration/index.rst new file mode 100644 index 000000000..85f588a20 --- /dev/null +++ b/doc/source/example/camera_calibration/index.rst @@ -0,0 +1,11 @@ +Camera Calibration +================== + +These are example files that provide examples on how to use OpenCSP for camera lens calibration. + + + +.. toctree:: + :maxdepth: 1 + + config.rst \ No newline at end of file diff --git a/doc/source/example/index.rst b/doc/source/example/index.rst index f2de14a32..065c1fbb4 100644 --- a/doc/source/example/index.rst +++ b/doc/source/example/index.rst @@ -6,6 +6,7 @@ This section describes the OpenCSP examples. .. toctree:: :maxdepth: 1 + camera_calibration/index.rst csp/index.rst mirror/index.rst scene_reconstruction/index.rst diff --git a/doc/source/example/sofast/config.rst b/doc/source/example/sofast/config.rst index 10529b6f8..67f1913c5 100644 --- a/doc/source/example/sofast/config.rst +++ b/doc/source/example/sofast/config.rst @@ -17,11 +17,3 @@ Undefined Facet Data Process :members: :show-inheritance: -View Camera Distortion -====================== - -.. currentmodule:: example.sofast_fringe.example_calibration_camera_pose - -.. automodule:: example.sofast_fringe.example_calibration_camera_pose - :members: - :show-inheritance: diff --git a/doc/source/example/sofast/index.rst b/doc/source/example/sofast/index.rst index 57aafcc25..bf1af56e3 100644 --- a/doc/source/example/sofast/index.rst +++ b/doc/source/example/sofast/index.rst @@ -1,7 +1,7 @@ -SoFast -====== +SOFAST Fringe +============= -These are example files that provide examples on how to use Sofast to collect data and process already collected data. +These are example files that provide examples on how to use Sofast Fringe to process collected data and perform system calibrations. diff --git a/doc/source/library_reference/app/sofast/calibration/config.rst b/doc/source/library_reference/app/sofast/calibration/config.rst deleted file mode 100644 index 6470f9364..000000000 --- a/doc/source/library_reference/app/sofast/calibration/config.rst +++ /dev/null @@ -1,19 +0,0 @@ -opencsp.app.sofast.lib.CalibrateDisplayShape -============================================================= - -.. currentmodule:: opencsp.app.sofast.lib.CalibrateDisplayShape - -.. automodule:: opencsp.app.sofast.lib.CalibrateDisplayShape - :members: - :undoc-members: - :show-inheritance: - -opencsp.app.sofast.lib.save_DisplayShape_file -=============================================================== - -.. currentmodule:: opencsp.app.sofast.lib.save_DisplayShape_file - -.. automodule:: opencsp.app.sofast.lib.save_DisplayShape_file - :members: - :undoc-members: - :show-inheritance: \ No newline at end of file diff --git a/doc/source/library_reference/app/sofast/calibration/index.rst b/doc/source/library_reference/app/sofast/calibration/index.rst deleted file mode 100644 index b5f060b40..000000000 --- a/doc/source/library_reference/app/sofast/calibration/index.rst +++ /dev/null @@ -1,10 +0,0 @@ -Calibration -=========== - -Library of photogrammetric algorithms used for SOFAST calibration. - - -.. toctree:: - :maxdepth: 1 - - config.rst \ No newline at end of file diff --git a/doc/source/library_reference/app/sofast/config.rst b/doc/source/library_reference/app/sofast/config.rst index 4ff735cb3..0eba6c6da 100644 --- a/doc/source/library_reference/app/sofast/config.rst +++ b/doc/source/library_reference/app/sofast/config.rst @@ -116,4 +116,14 @@ opencsp.app.sofast.SofastGUI .. automodule:: opencsp.app.sofast.SofastGUI :members: :undoc-members: - :show-inheritance: \ No newline at end of file + :show-inheritance: + +opencsp.app.sofast.lib.CalibrateDisplayShape +============================================================= + +.. currentmodule:: opencsp.app.sofast.lib.CalibrateDisplayShape + +.. automodule:: opencsp.app.sofast.lib.CalibrateDisplayShape + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/library_reference/app/sofast/index.rst b/doc/source/library_reference/app/sofast/index.rst index e389f546d..a74c1cdd9 100644 --- a/doc/source/library_reference/app/sofast/index.rst +++ b/doc/source/library_reference/app/sofast/index.rst @@ -1,4 +1,4 @@ -sofast +SOFAST ====== High-precision deflectometry measurement of CSP mirrors, including heliostat facets, full heliostats, and dishes. @@ -7,4 +7,3 @@ High-precision deflectometry measurement of CSP mirrors, including heliostat fac :maxdepth: 1 config.rst - calibration/index.rst \ No newline at end of file diff --git a/example/camera_calibration/example_view_camera_distortion.py b/example/camera_calibration/example_view_camera_distortion.py index 2476ee561..1120ffd79 100644 --- a/example/camera_calibration/example_view_camera_distortion.py +++ b/example/camera_calibration/example_view_camera_distortion.py @@ -7,7 +7,7 @@ from opencsp.common.lib.opencsp_path.opencsp_root_path import opencsp_code_dir -def example_driver(): +def example_show_camera_distortion(): """Example SOFAST script Plots visualization of camera distortion given a saved Camera HDF file @@ -38,4 +38,4 @@ def example_driver(): if __name__ == '__main__': - example_driver() + example_show_camera_distortion() From e9fe47c7c41a9ce668af3a7d9211fec8bb5843c2 Mon Sep 17 00:00:00 2001 From: Braden Date: Mon, 25 Mar 2024 14:28:33 -0600 Subject: [PATCH 28/36] Updated CalibrateDisplayShape documentation. --- opencsp/app/sofast/lib/CalibrateDisplayShape.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/opencsp/app/sofast/lib/CalibrateDisplayShape.py b/opencsp/app/sofast/lib/CalibrateDisplayShape.py index 92dd9f1e4..176198388 100644 --- a/opencsp/app/sofast/lib/CalibrateDisplayShape.py +++ b/opencsp/app/sofast/lib/CalibrateDisplayShape.py @@ -339,8 +339,7 @@ def get_data(self) -> dict: return {'xy_screen_fraction': pts_xy_screen_fraction, 'xyz_screen_coords': pts_xyz_screen} def as_DisplayShape(self, name: str) -> DisplayShape: - """Saves data to DisplayShape hdf file using the distorted3d model, see - DisplayShape documentation for more information. + """Returns calibrated DisplayShape object. Parameters ---------- From 3c308a527b17f086e4e0bf25443b993a6b8b7c89 Mon Sep 17 00:00:00 2001 From: Braden Date: Mon, 25 Mar 2024 14:30:33 -0600 Subject: [PATCH 29/36] Changed as ht to as h5 in imports. --- opencsp/app/sofast/lib/DisplayShape.py | 14 +++++++------- opencsp/app/sofast/lib/SpatialOrientation.py | 10 +++++----- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/opencsp/app/sofast/lib/DisplayShape.py b/opencsp/app/sofast/lib/DisplayShape.py index 04cf0f671..e07a2056b 100644 --- a/opencsp/app/sofast/lib/DisplayShape.py +++ b/opencsp/app/sofast/lib/DisplayShape.py @@ -3,10 +3,10 @@ from opencsp.common.lib.geometry.Vxy import Vxy from opencsp.common.lib.geometry.Vxyz import Vxyz -import opencsp.common.lib.tool.hdf5_tools as ht +import opencsp.common.lib.tool.hdf5_tools as h5 -class DisplayShape(ht.HDF5_IO_Abstract): +class DisplayShape(h5.HDF5_IO_Abstract): """Representation of a screen/projector for deflectometry.""" def __init__(self, grid_data: dict, name: str = '') -> 'DisplayShape': @@ -178,24 +178,24 @@ def load_from_hdf(cls, file: str, prefix: str = '') -> 'DisplayShape': """ # Load grid data datasets = [prefix + 'DisplayShape/screen_model', prefix + 'DisplayShape/name'] - data = ht.load_hdf5_datasets(datasets, file) + data = h5.load_hdf5_datasets(datasets, file) # Rectangular if data['screen_model'] == 'rectangular2D': datasets = [prefix + 'DisplayShape/screen_x', prefix + 'DisplayShape/screen_y'] - grid_data = ht.load_hdf5_datasets(datasets, file) + grid_data = h5.load_hdf5_datasets(datasets, file) # Distorted 2D elif data['screen_model'] == 'distorted2D': datasets = [prefix + 'DisplayShape/xy_screen_fraction', prefix + 'DisplayShape/xy_screen_coords'] - grid_data = ht.load_hdf5_datasets(datasets, file) + grid_data = h5.load_hdf5_datasets(datasets, file) grid_data['xy_screen_fraction'] = Vxy(grid_data['xy_screen_fraction']) grid_data['xy_screen_coords'] = Vxy(grid_data['xy_screen_coords']) # Distorted 3D elif data['screen_model'] == 'distorted3D': datasets = [prefix + 'DisplayShape/xy_screen_fraction', prefix + 'DisplayShape/xyz_screen_coords'] - grid_data = ht.load_hdf5_datasets(datasets, file) + grid_data = h5.load_hdf5_datasets(datasets, file) grid_data['xy_screen_fraction'] = Vxy(grid_data['xy_screen_fraction']) grid_data['xyz_screen_coords'] = Vxyz(grid_data['xyz_screen_coords']) @@ -232,4 +232,4 @@ def save_to_hdf(self, file: str, prefix: str = '') -> None: data.append(self.name) # Save data - ht.save_hdf5_datasets(data, datasets, file) + h5.save_hdf5_datasets(data, datasets, file) diff --git a/opencsp/app/sofast/lib/SpatialOrientation.py b/opencsp/app/sofast/lib/SpatialOrientation.py index 18801dd27..1b451313f 100644 --- a/opencsp/app/sofast/lib/SpatialOrientation.py +++ b/opencsp/app/sofast/lib/SpatialOrientation.py @@ -2,10 +2,10 @@ from opencsp.common.lib.geometry.TransformXYZ import TransformXYZ from opencsp.common.lib.geometry.Vxyz import Vxyz -import opencsp.common.lib.tool.hdf5_tools as ht +import opencsp.common.lib.tool.hdf5_tools as h5 -class SpatialOrientation(ht.HDF5_IO_Abstract): +class SpatialOrientation(h5.HDF5_IO_Abstract): """Holds relative orientations of camera, screen, and optic for deflectometry systems""" def __init__(self, r_cam_screen: Rotation, v_cam_screen_cam: Vxyz) -> 'SpatialOrientation': @@ -163,7 +163,7 @@ def save_to_hdf(self, file: str, prefix: str = '') -> None: ] data = [self.r_cam_screen.as_rotvec(), self.v_cam_screen_cam.data, self.optic_oriented] - ht.save_hdf5_datasets(data, datasets, file) + h5.save_hdf5_datasets(data, datasets, file) @classmethod def load_from_hdf(cls, file: str, prefix: str = '') -> 'SpatialOrientation': @@ -182,7 +182,7 @@ def load_from_hdf(cls, file: str, prefix: str = '') -> 'SpatialOrientation': prefix + 'SpatialOrientation/v_cam_screen_cam', prefix + 'SpatialOrientation/optic_oriented', ] - data = ht.load_hdf5_datasets(datasets, file) + data = h5.load_hdf5_datasets(datasets, file) r_cam_screen = Rotation.from_rotvec(data['r_cam_screen']) v_cam_screen_cam = Vxyz(data['v_cam_screen_cam']) ori = cls(r_cam_screen, v_cam_screen_cam) @@ -190,7 +190,7 @@ def load_from_hdf(cls, file: str, prefix: str = '') -> 'SpatialOrientation': # If optic is oriented, load optic orientation information if data['optic_oriented']: datasets = [prefix + 'SpatialOrientation/r_cam_optic', prefix + 'SpatialOrientation/v_cam_optic_cam'] - data = ht.load_hdf5_datasets(datasets, file) + data = h5.load_hdf5_datasets(datasets, file) r_cam_optic = Rotation.from_rotvec(data['r_cam_optic']) v_cam_optic_cam = Vxyz(data['v_cam_optic_cam']) ori.orient_optic_cam(r_cam_optic, v_cam_optic_cam) From c7a3cbd972a037380455b636c652ccab26ebfe8e Mon Sep 17 00:00:00 2001 From: Braden Date: Mon, 25 Mar 2024 15:15:43 -0600 Subject: [PATCH 30/36] Modified unit test functionalities. --- opencsp/app/sofast/test/test_DisplayShape.py | 32 +++++++++++++++---- .../sofast/test/test_SpatialOrientation.py | 6 ++-- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/opencsp/app/sofast/test/test_DisplayShape.py b/opencsp/app/sofast/test/test_DisplayShape.py index a965b2e10..071f9ca92 100644 --- a/opencsp/app/sofast/test/test_DisplayShape.py +++ b/opencsp/app/sofast/test/test_DisplayShape.py @@ -99,7 +99,7 @@ def test_distorted3D(self): def test_save_load_hdf_dist_3d(self): # Instantiate display object - name = 'Test DisplayShape' + name = 'Test DisplayShape 3D' disp = DisplayShape(self.grid_data_3D, name) file = join(self.save_dir, 'test_display_shape_dist_3d.h5') @@ -107,11 +107,18 @@ def test_save_load_hdf_dist_3d(self): disp.save_to_hdf(file) # Load - DisplayShape.load_from_hdf(file) + disp_load = DisplayShape.load_from_hdf(file) + + # Compare + self.assertEqual(disp.grid_data['screen_model'], disp_load.grid_data['screen_model']) + self.assertEqual(disp.name, disp_load.name) + np.testing.assert_equal( + disp.grid_data['xy_screen_fraction'].data, disp_load.grid_data['xy_screen_fraction'].data) + np.testing.assert_equal(disp.grid_data['xyz_screen_coords'].data, disp_load.grid_data['xyz_screen_coords'].data) def test_save_load_hdf_dist_2d(self): # Instantiate display object - name = 'Test DisplayShape' + name = 'Test DisplayShape 2D' disp = DisplayShape(self.grid_data_2D, name) file = join(self.save_dir, 'test_display_shape_dist_2d.h5') @@ -119,11 +126,18 @@ def test_save_load_hdf_dist_2d(self): disp.save_to_hdf(file) # Load - DisplayShape.load_from_hdf(file) + disp_load = DisplayShape.load_from_hdf(file) + + # Compare + self.assertEqual(disp.grid_data['screen_model'], disp_load.grid_data['screen_model']) + self.assertEqual(disp.name, disp_load.name) + np.testing.assert_equal( + disp.grid_data['xy_screen_fraction'].data, disp_load.grid_data['xy_screen_fraction'].data) + np.testing.assert_equal(disp.grid_data['xy_screen_coords'].data, disp_load.grid_data['xy_screen_coords'].data) def test_save_load_hdf_rectangular(self): # Instantiate display object - name = 'Test DisplayShape' + name = 'Test DisplayShape Rectangular' disp = DisplayShape(self.grid_data_rect2D, name) file = join(self.save_dir, 'test_display_shape_rect.h5') @@ -131,7 +145,13 @@ def test_save_load_hdf_rectangular(self): disp.save_to_hdf(file) # Load - DisplayShape.load_from_hdf(file) + disp_load = DisplayShape.load_from_hdf(file) + + # Compare + self.assertEqual(disp.grid_data['screen_model'], disp_load.grid_data['screen_model']) + self.assertEqual(disp.name, disp_load.name) + self.assertEqual(disp.grid_data['screen_x'], disp_load.grid_data['screen_x']) + self.assertEqual(disp.grid_data['screen_y'], disp_load.grid_data['screen_y']) if __name__ == '__main__': diff --git a/opencsp/app/sofast/test/test_SpatialOrientation.py b/opencsp/app/sofast/test/test_SpatialOrientation.py index 97d1196ba..2e705512a 100644 --- a/opencsp/app/sofast/test/test_SpatialOrientation.py +++ b/opencsp/app/sofast/test/test_SpatialOrientation.py @@ -78,7 +78,7 @@ def test_io_oriented_optic(self): # Load ori = SpatialOrientation.load_from_hdf(file) # Check optic is oriented - np.testing.assert_equal(ori.optic_oriented, True) + self.assertEqual(ori.optic_oriented, True) def test_io_unoriented_optic(self): file = join(self.save_dir, 'test_spatial_orientation_unoriented_optic.h5') @@ -90,8 +90,8 @@ def test_io_unoriented_optic(self): # Load ori_2 = SpatialOrientation.load_from_hdf(file) # Check optic not oriented - np.testing.assert_equal(ori_1.optic_oriented, False) - np.testing.assert_equal(ori_2.optic_oriented, False) + self.assertEqual(ori_1.optic_oriented, False) + self.assertEqual(ori_2.optic_oriented, False) if __name__ == '__main__': From 4fe084cbffcceed6fb786f8673c0b1237b0ca241 Mon Sep 17 00:00:00 2001 From: Braden Date: Mon, 25 Mar 2024 15:20:56 -0600 Subject: [PATCH 31/36] Updates to be black compliant. --- opencsp/app/sofast/test/test_DisplayShape.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/opencsp/app/sofast/test/test_DisplayShape.py b/opencsp/app/sofast/test/test_DisplayShape.py index 071f9ca92..a43f0d808 100644 --- a/opencsp/app/sofast/test/test_DisplayShape.py +++ b/opencsp/app/sofast/test/test_DisplayShape.py @@ -113,7 +113,8 @@ def test_save_load_hdf_dist_3d(self): self.assertEqual(disp.grid_data['screen_model'], disp_load.grid_data['screen_model']) self.assertEqual(disp.name, disp_load.name) np.testing.assert_equal( - disp.grid_data['xy_screen_fraction'].data, disp_load.grid_data['xy_screen_fraction'].data) + disp.grid_data['xy_screen_fraction'].data, disp_load.grid_data['xy_screen_fraction'].data + ) np.testing.assert_equal(disp.grid_data['xyz_screen_coords'].data, disp_load.grid_data['xyz_screen_coords'].data) def test_save_load_hdf_dist_2d(self): @@ -132,7 +133,8 @@ def test_save_load_hdf_dist_2d(self): self.assertEqual(disp.grid_data['screen_model'], disp_load.grid_data['screen_model']) self.assertEqual(disp.name, disp_load.name) np.testing.assert_equal( - disp.grid_data['xy_screen_fraction'].data, disp_load.grid_data['xy_screen_fraction'].data) + disp.grid_data['xy_screen_fraction'].data, disp_load.grid_data['xy_screen_fraction'].data + ) np.testing.assert_equal(disp.grid_data['xy_screen_coords'].data, disp_load.grid_data['xy_screen_coords'].data) def test_save_load_hdf_rectangular(self): From e7248257a23a776c307ce65a5729df961d621f05 Mon Sep 17 00:00:00 2001 From: Braden Date: Thu, 28 Mar 2024 22:28:18 -0600 Subject: [PATCH 32/36] Added optic_oriented to spatial_orientation unit test data. --- .../spatial_orientation.h5 | Bin 182024 -> 184200 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/opencsp/test/data/measurements_sofast_fixed/spatial_orientation.h5 b/opencsp/test/data/measurements_sofast_fixed/spatial_orientation.h5 index 61859cc1e64fe43fe837a4798ac697784cd8f081..b7147e918c270eb208a5a550d5dbeba6ad5ee472 100644 GIT binary patch delta 207 zcmeC^H+H<`?rKYw(-DjMn6Ud7O>_5 zs9Ht_4loO5ATxsoSYCn&Vu=7y8Y0KR;O6KP>%R2*-gcg(?BHDgp+<0Wg;_!2&-50RWMzCX;{>AeXqo0`>s{ Sx7We~83B>ND7Wy#0vH2fycGHX From cd8dbb3733b24bd3b73e88264dfb29de50aa615f Mon Sep 17 00:00:00 2001 From: Braden Date: Thu, 28 Mar 2024 22:37:02 -0600 Subject: [PATCH 33/36] Changed hf to h5 in test_ProcessSofastFixed --- opencsp/app/sofast/test/test_ProcessSofastFixed.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/opencsp/app/sofast/test/test_ProcessSofastFixed.py b/opencsp/app/sofast/test/test_ProcessSofastFixed.py index 79e3355fd..23d4f11ee 100644 --- a/opencsp/app/sofast/test/test_ProcessSofastFixed.py +++ b/opencsp/app/sofast/test/test_ProcessSofastFixed.py @@ -18,7 +18,7 @@ import opencsp.common.lib.render.figure_management as fm import opencsp.common.lib.render_control.RenderControlAxis as rca import opencsp.common.lib.render_control.RenderControlFigure as rcfg -import opencsp.common.lib.tool.hdf5_tools as ht +import opencsp.common.lib.tool.hdf5_tools as h5 import opencsp.common.lib.tool.file_tools as ft import opencsp.common.lib.tool.log_tools as lt @@ -53,7 +53,7 @@ def setUpClass(cls): # Load expected data datasets = ['CalculationsFixedPattern/Facet_000/SlopeSolverData/slopes_facet_xy'] - data = ht.load_hdf5_datasets(datasets, file_exp) + data = h5.load_hdf5_datasets(datasets, file_exp) cls.exp_slopes_xy = data['slopes_facet_xy'] # Instantiate class From f11738c28f2ca5086c3c9e428356610a1a9ca04f Mon Sep 17 00:00:00 2001 From: Braden Date: Thu, 28 Mar 2024 22:38:18 -0600 Subject: [PATCH 34/36] Changed how spatial orientation is saved in calculation data classes. --- opencsp/app/sofast/lib/calculation_data_classes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opencsp/app/sofast/lib/calculation_data_classes.py b/opencsp/app/sofast/lib/calculation_data_classes.py index f657f4432..855c452f7 100644 --- a/opencsp/app/sofast/lib/calculation_data_classes.py +++ b/opencsp/app/sofast/lib/calculation_data_classes.py @@ -105,7 +105,7 @@ def save_to_hdf(self, file: str, prefix: str = ''): prefix + 'CalculationDataGeometryFacet/u_pixel_pointing_facet', prefix + 'CalculationDataGeometryFacet/v_screen_points_facet', ] - self.spatial_orientation.save_all_to_hdf(file, prefix + 'CalculationDataGeometryFacet/') + self.spatial_orientation.save_to_hdf(file, prefix + 'CalculationDataGeometryFacet/') _save_data_in_file(data, datasets, file) From 358ed5a64cfb4d42f6de21adc07a943378efe4c9 Mon Sep 17 00:00:00 2001 From: Braden Date: Thu, 28 Mar 2024 22:52:03 -0600 Subject: [PATCH 35/36] Removed unnecessary save_data_as_hdf method from CalibrateDisplayShape --- opencsp/app/sofast/lib/CalibrateDisplayShape.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/opencsp/app/sofast/lib/CalibrateDisplayShape.py b/opencsp/app/sofast/lib/CalibrateDisplayShape.py index 176198388..0b58fe93f 100644 --- a/opencsp/app/sofast/lib/CalibrateDisplayShape.py +++ b/opencsp/app/sofast/lib/CalibrateDisplayShape.py @@ -350,17 +350,6 @@ def as_DisplayShape(self, name: str) -> DisplayShape: grid_data.update({'screen_model': 'distorted3D'}) return DisplayShape(grid_data, name) - def save_data_as_hdf(self, file: str) -> None: - """Saves distortion data to given HDF file""" - # Get data - data = self.get_data() - - # Save distortion data - with h5py.File(file, 'w') as f: - f.create_dataset('xy_screen_fraction', data=data['xy_screen_fraction'].data) - f.create_dataset('xyz_screen_coords', data=data['xyz_screen_coords'].data) - print(f'Saved distortion data to: {os.path.abspath(file):s}') - def visualize_located_cameras(self) -> None: """Plots cameras and alignment points""" # Visualize located cameras From de95ab24ab9bb669e66570b62f5bd770c55251c0 Mon Sep 17 00:00:00 2001 From: Braden Date: Thu, 28 Mar 2024 23:04:20 -0600 Subject: [PATCH 36/36] Updated sofastFringe examples to use Spatial Orientation object. --- .../example_process_facet_ensemble.py | 13 +++---------- .../sofast_fringe/example_process_single_facet.py | 13 +++---------- .../example_process_undefined_shape.py | 15 ++++----------- 3 files changed, 10 insertions(+), 31 deletions(-) diff --git a/example/sofast_fringe/example_process_facet_ensemble.py b/example/sofast_fringe/example_process_facet_ensemble.py index fc6a02aa8..65bb73fe0 100644 --- a/example/sofast_fringe/example_process_facet_ensemble.py +++ b/example/sofast_fringe/example_process_facet_ensemble.py @@ -1,7 +1,5 @@ from os.path import join, dirname -import matplotlib - from opencsp.app.sofast.lib.DisplayShape import DisplayShape as Display from opencsp.app.sofast.lib.DefinitionEnsemble import DefinitionEnsemble from opencsp.app.sofast.lib.DefinitionFacet import DefinitionFacet @@ -9,7 +7,6 @@ from opencsp.app.sofast.lib.MeasurementSofastFringe import MeasurementSofastFringe from opencsp.app.sofast.lib.ProcessSofastFringe import ProcessSofastFringe as Sofast from opencsp.app.sofast.lib.SpatialOrientation import SpatialOrientation -from opencsp.app.sofast.lib.visualize_setup import visualize_setup from opencsp.common.lib.camera.Camera import Camera from opencsp.common.lib.csp.FacetEnsemble import FacetEnsemble from opencsp.common.lib.deflectometry.Surface2DParabolic import Surface2DParabolic @@ -39,6 +36,7 @@ def example(dir_save: str): file_measurement = join(sample_data_dir, 'measurement_ensemble.h5') file_camera = join(sample_data_dir, 'camera.h5') file_display = join(sample_data_dir, 'display_distorted_2d.h5') + file_orientation = join(sample_data_dir, 'spatial_orientation.h5') file_calibration = join(sample_data_dir, 'image_calibration.h5') file_facet = join(sample_data_dir, 'Facet_lab_6x4.json') file_ensemble = join(sample_data_dir, 'Ensemble_lab_6x4.json') @@ -46,6 +44,7 @@ def example(dir_save: str): # Load data camera = Camera.load_from_hdf(file_camera) display = Display.load_from_hdf(file_display) + orientation = SpatialOrientation.load_from_hdf(file_orientation) measurement = MeasurementSofastFringe.load_from_hdf(file_measurement) calibration = ImageCalibrationScaling.load_from_hdf(file_calibration) ensemble_data = DefinitionEnsemble.load_from_json(file_ensemble) @@ -62,7 +61,7 @@ def example(dir_save: str): measurement.calibrate_fringe_images(calibration) # Instantiate sofast object - sofast = Sofast(measurement, camera, display) + sofast = Sofast(measurement, orientation, camera, display) # Update search parameters sofast.params.mask_hist_thresh = 0.83 @@ -88,12 +87,6 @@ def example(dir_save: str): mirror_control = rcm.RenderControlMirror(centroid=True, surface_normals=True, norm_res=1) axis_control_m = rca.meters() - # Visualize setup - fig_record = fm.setup_figure_for_3d_data(figure_control, axis_control_m, title='') - spatial_ori: SpatialOrientation = sofast.data_geometry_facet[0].spatial_orientation - visualize_setup(display, camera, spatial_ori.v_screen_optic_screen, spatial_ori.r_optic_screen, ax=fig_record.axis) - fig_record.save(dir_save, 'physical_setup_layout', 'png') - # Plot scenario fig_record = fm.setup_figure_for_3d_data(figure_control, axis_control_m, title='Facet Ensemble') ensemble.draw(fig_record.view, mirror_control) diff --git a/example/sofast_fringe/example_process_single_facet.py b/example/sofast_fringe/example_process_single_facet.py index 18dbf7311..31a600bca 100644 --- a/example/sofast_fringe/example_process_single_facet.py +++ b/example/sofast_fringe/example_process_single_facet.py @@ -1,14 +1,11 @@ from os.path import join, dirname -import matplotlib - from opencsp.app.sofast.lib.DisplayShape import DisplayShape as Display from opencsp.app.sofast.lib.DefinitionFacet import DefinitionFacet from opencsp.app.sofast.lib.ImageCalibrationScaling import ImageCalibrationScaling from opencsp.app.sofast.lib.MeasurementSofastFringe import MeasurementSofastFringe from opencsp.app.sofast.lib.ProcessSofastFringe import ProcessSofastFringe as Sofast from opencsp.app.sofast.lib.SpatialOrientation import SpatialOrientation -from opencsp.app.sofast.lib.visualize_setup import visualize_setup from opencsp.common.lib.camera.Camera import Camera from opencsp.common.lib.csp.Facet import Facet from opencsp.common.lib.deflectometry.Surface2DParabolic import Surface2DParabolic @@ -36,12 +33,14 @@ def example(dir_save: str): file_measurement = join(sample_data_dir, 'measurement_facet.h5') file_camera = join(sample_data_dir, 'camera.h5') file_display = join(sample_data_dir, 'display_distorted_2d.h5') + file_orientation = join(sample_data_dir, 'spatial_orientation.h5') file_calibration = join(sample_data_dir, 'image_calibration.h5') file_facet = join(sample_data_dir, 'Facet_NSTTF.json') # Load data camera = Camera.load_from_hdf(file_camera) display = Display.load_from_hdf(file_display) + orientation = SpatialOrientation.load_from_hdf(file_orientation) measurement = MeasurementSofastFringe.load_from_hdf(file_measurement) calibration = ImageCalibrationScaling.load_from_hdf(file_calibration) facet_data = DefinitionFacet.load_from_json(file_facet) @@ -53,7 +52,7 @@ def example(dir_save: str): measurement.calibrate_fringe_images(calibration) # Instantiate sofast object - sofast = Sofast(measurement, camera, display) + sofast = Sofast(measurement, orientation, camera, display) # Process sofast.process_optic_singlefacet(facet_data, surface) @@ -70,12 +69,6 @@ def example(dir_save: str): figure_control = rcfg.RenderControlFigure(tile_array=(1, 1), tile_square=True) axis_control_m = rca.meters() - # Visualize setup - fig_record = fm.setup_figure_for_3d_data(figure_control, axis_control_m, title='') - spatial_ori: SpatialOrientation = sofast.data_geometry_facet[0].spatial_orientation - visualize_setup(display, camera, spatial_ori.v_screen_optic_screen, spatial_ori.r_optic_screen, ax=fig_record.axis) - fig_record.save(dir_save, 'physical_setup_layout', 'png') - # Plot slope map fig_record = fm.setup_figure(figure_control, axis_control_m, title='') facet.plot_orthorectified_slope(res=0.002, clim=7, axis=fig_record.axis) diff --git a/example/sofast_fringe/example_process_undefined_shape.py b/example/sofast_fringe/example_process_undefined_shape.py index 45c7310fd..10a3ea3ac 100644 --- a/example/sofast_fringe/example_process_undefined_shape.py +++ b/example/sofast_fringe/example_process_undefined_shape.py @@ -1,8 +1,5 @@ from os.path import join, dirname -import matplotlib - -from opencsp.app.sofast.lib.visualize_setup import visualize_setup from opencsp.app.sofast.lib.ImageCalibrationScaling import ImageCalibrationScaling from opencsp.app.sofast.lib.MeasurementSofastFringe import MeasurementSofastFringe from opencsp.app.sofast.lib.ProcessSofastFringe import ProcessSofastFringe as Sofast @@ -35,11 +32,13 @@ def example(dir_save): file_measurement = join(sample_data_dir, 'measurement_facet.h5') file_camera = join(sample_data_dir, 'camera.h5') file_display = join(sample_data_dir, 'display_distorted_2d.h5') + file_orientation = join(sample_data_dir, 'spatial_orientation.h5') file_calibration = join(sample_data_dir, 'image_calibration.h5') # Load data camera = Camera.load_from_hdf(file_camera) display = Display.load_from_hdf(file_display) + orientation = SpatialOrientation.load_from_hdf(file_orientation) measurement = MeasurementSofastFringe.load_from_hdf(file_measurement) calibration = ImageCalibrationScaling.load_from_hdf(file_calibration) @@ -50,7 +49,7 @@ def example(dir_save): measurement.calibrate_fringe_images(calibration) # Instantiate sofast object - sofast = Sofast(measurement, camera, display) + sofast = Sofast(measurement, orientation, camera, display) sofast.params.mask_keep_largest_area = True # Process @@ -68,12 +67,6 @@ def example(dir_save): figure_control = rcfg.RenderControlFigure(tile_array=(1, 1), tile_square=True) axis_control_m = rca.meters() - # Visualize setup - fig_record = fm.setup_figure_for_3d_data(figure_control, axis_control_m, title='') - spatial_ori: SpatialOrientation = sofast.data_geometry_facet[0].spatial_orientation - visualize_setup(display, camera, spatial_ori.v_screen_optic_screen, spatial_ori.r_optic_screen, ax=fig_record.axis) - fig_record.save(dir_save, 'physical_setup_layout', 'png') - # Plot slope map fig_record = fm.setup_figure(figure_control, axis_control_m, title='') facet.plot_orthorectified_slope(res=0.002, clim=7, axis=fig_record.axis) @@ -85,7 +78,7 @@ def example(dir_save): def example_driver(): # Define save dir - save_path = join(dirname(__file__), 'data/output/single_facet') + save_path = join(dirname(__file__), 'data/output/undefined_shape') ft.create_directories_if_necessary(save_path) # Set up logger