pyvale 2025.5.3__cp311-cp311-musllinux_1_2_aarch64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of pyvale might be problematic. Click here for more details.

Files changed (174) hide show
  1. pyvale/__init__.py +89 -0
  2. pyvale/analyticmeshgen.py +102 -0
  3. pyvale/analyticsimdatafactory.py +91 -0
  4. pyvale/analyticsimdatagenerator.py +323 -0
  5. pyvale/blendercalibrationdata.py +15 -0
  6. pyvale/blenderlightdata.py +26 -0
  7. pyvale/blendermaterialdata.py +15 -0
  8. pyvale/blenderrenderdata.py +30 -0
  9. pyvale/blenderscene.py +488 -0
  10. pyvale/blendertools.py +420 -0
  11. pyvale/camera.py +146 -0
  12. pyvale/cameradata.py +69 -0
  13. pyvale/cameradata2d.py +84 -0
  14. pyvale/camerastereo.py +217 -0
  15. pyvale/cameratools.py +522 -0
  16. pyvale/cython/rastercyth.c +32211 -0
  17. pyvale/cython/rastercyth.cpython-311-aarch64-linux-musl.so +0 -0
  18. pyvale/cython/rastercyth.py +640 -0
  19. pyvale/data/__init__.py +5 -0
  20. pyvale/data/cal_target.tiff +0 -0
  21. pyvale/data/case00_HEX20_out.e +0 -0
  22. pyvale/data/case00_HEX27_out.e +0 -0
  23. pyvale/data/case00_HEX8_out.e +0 -0
  24. pyvale/data/case00_TET10_out.e +0 -0
  25. pyvale/data/case00_TET14_out.e +0 -0
  26. pyvale/data/case00_TET4_out.e +0 -0
  27. pyvale/data/case13_out.e +0 -0
  28. pyvale/data/case16_out.e +0 -0
  29. pyvale/data/case17_out.e +0 -0
  30. pyvale/data/case18_1_out.e +0 -0
  31. pyvale/data/case18_2_out.e +0 -0
  32. pyvale/data/case18_3_out.e +0 -0
  33. pyvale/data/case25_out.e +0 -0
  34. pyvale/data/case26_out.e +0 -0
  35. pyvale/data/optspeckle_2464x2056px_spec5px_8bit_gblur1px.tiff +0 -0
  36. pyvale/dataset.py +325 -0
  37. pyvale/errorcalculator.py +109 -0
  38. pyvale/errordriftcalc.py +146 -0
  39. pyvale/errorintegrator.py +336 -0
  40. pyvale/errorrand.py +607 -0
  41. pyvale/errorsyscalib.py +134 -0
  42. pyvale/errorsysdep.py +327 -0
  43. pyvale/errorsysfield.py +414 -0
  44. pyvale/errorsysindep.py +808 -0
  45. pyvale/examples/__init__.py +5 -0
  46. pyvale/examples/basics/ex1_1_basicscalars_therm2d.py +131 -0
  47. pyvale/examples/basics/ex1_2_sensormodel_therm2d.py +158 -0
  48. pyvale/examples/basics/ex1_3_customsens_therm3d.py +216 -0
  49. pyvale/examples/basics/ex1_4_basicerrors_therm3d.py +153 -0
  50. pyvale/examples/basics/ex1_5_fielderrs_therm3d.py +168 -0
  51. pyvale/examples/basics/ex1_6_caliberrs_therm2d.py +133 -0
  52. pyvale/examples/basics/ex1_7_spatavg_therm2d.py +123 -0
  53. pyvale/examples/basics/ex2_1_basicvectors_disp2d.py +112 -0
  54. pyvale/examples/basics/ex2_2_vectorsens_disp2d.py +111 -0
  55. pyvale/examples/basics/ex2_3_sensangle_disp2d.py +139 -0
  56. pyvale/examples/basics/ex2_4_chainfielderrs_disp2d.py +196 -0
  57. pyvale/examples/basics/ex2_5_vectorfields3d_disp3d.py +109 -0
  58. pyvale/examples/basics/ex3_1_basictensors_strain2d.py +114 -0
  59. pyvale/examples/basics/ex3_2_tensorsens2d_strain2d.py +111 -0
  60. pyvale/examples/basics/ex3_3_tensorsens3d_strain3d.py +182 -0
  61. pyvale/examples/basics/ex4_1_expsim2d_thermmech2d.py +171 -0
  62. pyvale/examples/basics/ex4_2_expsim3d_thermmech3d.py +252 -0
  63. pyvale/examples/genanalyticdata/ex1_1_scalarvisualisation.py +35 -0
  64. pyvale/examples/genanalyticdata/ex1_2_scalarcasebuild.py +43 -0
  65. pyvale/examples/genanalyticdata/ex2_1_analyticsensors.py +80 -0
  66. pyvale/examples/imagedef2d/ex_imagedef2d_todisk.py +79 -0
  67. pyvale/examples/renderblender/ex1_1_blenderscene.py +121 -0
  68. pyvale/examples/renderblender/ex1_2_blenderdeformed.py +119 -0
  69. pyvale/examples/renderblender/ex2_1_stereoscene.py +128 -0
  70. pyvale/examples/renderblender/ex2_2_stereodeformed.py +131 -0
  71. pyvale/examples/renderblender/ex3_1_blendercalibration.py +120 -0
  72. pyvale/examples/renderrasterisation/ex_rastenp.py +153 -0
  73. pyvale/examples/renderrasterisation/ex_rastercyth_oneframe.py +218 -0
  74. pyvale/examples/renderrasterisation/ex_rastercyth_static_cypara.py +187 -0
  75. pyvale/examples/renderrasterisation/ex_rastercyth_static_pypara.py +190 -0
  76. pyvale/examples/visualisation/ex1_1_plot_traces.py +102 -0
  77. pyvale/examples/visualisation/ex2_1_animate_sim.py +89 -0
  78. pyvale/experimentsimulator.py +175 -0
  79. pyvale/field.py +128 -0
  80. pyvale/fieldconverter.py +351 -0
  81. pyvale/fieldsampler.py +111 -0
  82. pyvale/fieldscalar.py +166 -0
  83. pyvale/fieldtensor.py +218 -0
  84. pyvale/fieldtransform.py +388 -0
  85. pyvale/fieldvector.py +213 -0
  86. pyvale/generatorsrandom.py +505 -0
  87. pyvale/imagedef2d.py +569 -0
  88. pyvale/integratorfactory.py +240 -0
  89. pyvale/integratorquadrature.py +217 -0
  90. pyvale/integratorrectangle.py +165 -0
  91. pyvale/integratorspatial.py +89 -0
  92. pyvale/integratortype.py +43 -0
  93. pyvale/output.py +17 -0
  94. pyvale/pyvaleexceptions.py +11 -0
  95. pyvale/raster.py +31 -0
  96. pyvale/rastercy.py +77 -0
  97. pyvale/rasternp.py +603 -0
  98. pyvale/rendermesh.py +147 -0
  99. pyvale/sensorarray.py +178 -0
  100. pyvale/sensorarrayfactory.py +196 -0
  101. pyvale/sensorarraypoint.py +278 -0
  102. pyvale/sensordata.py +71 -0
  103. pyvale/sensordescriptor.py +213 -0
  104. pyvale/sensortools.py +142 -0
  105. pyvale/simcases/case00_HEX20.i +242 -0
  106. pyvale/simcases/case00_HEX27.i +242 -0
  107. pyvale/simcases/case00_HEX8.i +242 -0
  108. pyvale/simcases/case00_TET10.i +242 -0
  109. pyvale/simcases/case00_TET14.i +242 -0
  110. pyvale/simcases/case00_TET4.i +242 -0
  111. pyvale/simcases/case01.i +101 -0
  112. pyvale/simcases/case02.i +156 -0
  113. pyvale/simcases/case03.i +136 -0
  114. pyvale/simcases/case04.i +181 -0
  115. pyvale/simcases/case05.i +234 -0
  116. pyvale/simcases/case06.i +305 -0
  117. pyvale/simcases/case07.geo +135 -0
  118. pyvale/simcases/case07.i +87 -0
  119. pyvale/simcases/case08.geo +144 -0
  120. pyvale/simcases/case08.i +153 -0
  121. pyvale/simcases/case09.geo +204 -0
  122. pyvale/simcases/case09.i +87 -0
  123. pyvale/simcases/case10.geo +204 -0
  124. pyvale/simcases/case10.i +257 -0
  125. pyvale/simcases/case11.geo +337 -0
  126. pyvale/simcases/case11.i +147 -0
  127. pyvale/simcases/case12.geo +388 -0
  128. pyvale/simcases/case12.i +329 -0
  129. pyvale/simcases/case13.i +140 -0
  130. pyvale/simcases/case14.i +159 -0
  131. pyvale/simcases/case15.geo +337 -0
  132. pyvale/simcases/case15.i +150 -0
  133. pyvale/simcases/case16.geo +391 -0
  134. pyvale/simcases/case16.i +357 -0
  135. pyvale/simcases/case17.geo +135 -0
  136. pyvale/simcases/case17.i +144 -0
  137. pyvale/simcases/case18.i +254 -0
  138. pyvale/simcases/case18_1.i +254 -0
  139. pyvale/simcases/case18_2.i +254 -0
  140. pyvale/simcases/case18_3.i +254 -0
  141. pyvale/simcases/case19.geo +252 -0
  142. pyvale/simcases/case19.i +99 -0
  143. pyvale/simcases/case20.geo +252 -0
  144. pyvale/simcases/case20.i +250 -0
  145. pyvale/simcases/case21.geo +74 -0
  146. pyvale/simcases/case21.i +155 -0
  147. pyvale/simcases/case22.geo +82 -0
  148. pyvale/simcases/case22.i +140 -0
  149. pyvale/simcases/case23.geo +164 -0
  150. pyvale/simcases/case23.i +140 -0
  151. pyvale/simcases/case24.geo +79 -0
  152. pyvale/simcases/case24.i +123 -0
  153. pyvale/simcases/case25.geo +82 -0
  154. pyvale/simcases/case25.i +140 -0
  155. pyvale/simcases/case26.geo +166 -0
  156. pyvale/simcases/case26.i +140 -0
  157. pyvale/simcases/run_1case.py +61 -0
  158. pyvale/simcases/run_all_cases.py +69 -0
  159. pyvale/simcases/run_build_case.py +64 -0
  160. pyvale/simcases/run_example_cases.py +69 -0
  161. pyvale/simtools.py +67 -0
  162. pyvale/visualexpplotter.py +191 -0
  163. pyvale/visualimagedef.py +74 -0
  164. pyvale/visualimages.py +76 -0
  165. pyvale/visualopts.py +493 -0
  166. pyvale/visualsimanimator.py +111 -0
  167. pyvale/visualsimsensors.py +318 -0
  168. pyvale/visualtools.py +136 -0
  169. pyvale/visualtraceplotter.py +142 -0
  170. pyvale-2025.5.3.dist-info/METADATA +144 -0
  171. pyvale-2025.5.3.dist-info/RECORD +174 -0
  172. pyvale-2025.5.3.dist-info/WHEEL +5 -0
  173. pyvale-2025.5.3.dist-info/licenses/LICENSE +21 -0
  174. pyvale-2025.5.3.dist-info/top_level.txt +1 -0
pyvale/simtools.py ADDED
@@ -0,0 +1,67 @@
1
+ # ==============================================================================
2
+ # pyvale: the python validation engine
3
+ # License: MIT
4
+ # Copyright (C) 2025 The Computer Aided Validation Team
5
+ # ==============================================================================
6
+ import numpy as np
7
+ from pyvale.rendermesh import RenderMeshData
8
+
9
+ class SimTools:
10
+ """Namespace for tools required for analysing simulation results.
11
+ """
12
+
13
+ @staticmethod
14
+ def centre_mesh_nodes(nodes: np.ndarray, spat_dim: int) -> np.ndarray:
15
+ """A method to centre the nodes of a mesh around the origin.
16
+
17
+ Parameters
18
+ ----------
19
+ nodes : np.ndarray
20
+ An array containing the node locations of the mesh.
21
+ spat_dim : int
22
+ The spatial dimension of the mesh.
23
+
24
+ Returns
25
+ -------
26
+ np.ndarray
27
+ An array containing the mesh node locations, but centred around
28
+ the origin.
29
+ """
30
+ max = np.max(nodes, axis=0)
31
+ min = np.min(nodes, axis=0)
32
+ middle = max - ((max - min) / 2)
33
+ if spat_dim == 3:
34
+ middle[2] = 0
35
+ centred = np.subtract(nodes, middle)
36
+ return centred
37
+
38
+ @staticmethod
39
+ def get_deformed_nodes(timestep: int,
40
+ render_mesh: RenderMeshData) -> np.ndarray | None:
41
+ """A method to obtain the deformed locations of all the nodes at a given
42
+ timestep.
43
+
44
+ Parameters
45
+ ----------
46
+ timestep : int
47
+ The timestep at which to find the deformed nodes.
48
+ render_mesh: RenderMeshData
49
+ A dataclass containing the skinned mesh and simulation results.
50
+
51
+ Returns
52
+ -------
53
+ np.ndarray | None
54
+ An array containing the deformed values of all the components at
55
+ each node location. Returns None if there are no deformation values.
56
+ """
57
+ if render_mesh.fields_disp is None:
58
+ return None
59
+
60
+ added_disp = render_mesh.fields_disp[:, timestep]
61
+ if added_disp.shape[1] == 2:
62
+ added_disp = np.hstack((added_disp,np.zeros([added_disp.shape[0],1])))
63
+ coords = np.delete(render_mesh.coords, 3, axis=1)
64
+ deformed_nodes = coords + added_disp
65
+ return deformed_nodes
66
+
67
+
@@ -0,0 +1,191 @@
1
+ # ==============================================================================
2
+ # pyvale: the python validation engine
3
+ # License: MIT
4
+ # Copyright (C) 2025 The Computer Aided Validation Team
5
+ # ==============================================================================
6
+
7
+ """
8
+ This module contains function for plotting virtuals sensor trace summary
9
+ statistics and uncertainty bounds over simulated experiments.
10
+ """
11
+
12
+ from typing import Any
13
+ import numpy as np
14
+ import matplotlib.pyplot as plt
15
+ from pyvale.pyvaleexceptions import VisError
16
+ from pyvale.visualopts import (PlotOptsGeneral,
17
+ TraceOptsExperiment,
18
+ EExpVisBounds,
19
+ EExpVisCentre)
20
+ from pyvale.experimentsimulator import ExperimentSimulator
21
+
22
+
23
+ def plot_exp_traces(exp_sim: ExperimentSimulator,
24
+ component: str,
25
+ sens_array_num: int,
26
+ sim_num: int,
27
+ trace_opts: TraceOptsExperiment | None = None,
28
+ plot_opts: PlotOptsGeneral | None = None) -> tuple[Any,Any]:
29
+ """Plots time traces for summary statistics of virtual sensor traces over
30
+ a series of virtual experiments.
31
+
32
+ Parameters
33
+ ----------
34
+ exp_sim : ExperimentSimulator
35
+ Experiment simulation object containing the set of virtual experiment to
36
+ be plotted.
37
+ component : str
38
+ String key for the field component to plot.
39
+ sens_array_num : int
40
+ List index for the sensor array to plot.
41
+ sim_num : int
42
+ Index for the simulation to plot.
43
+ trace_opts : TraceOptsExperiment | None, optional
44
+ Dataclass containing specific options for controlling the plot
45
+ appearance, by default None. If None the default options are used.
46
+ plot_opts : PlotOptsGeneral | None, optional
47
+ Dataclass containing general options for formatting plots and
48
+ visualisations, by default None. If None the default options are used.
49
+
50
+ Returns
51
+ -------
52
+ tuple[Any,Any]
53
+ A tuple containing a handle to the matplotlib figure and axis objects:
54
+ (fig,ax).
55
+
56
+ Raises
57
+ ------
58
+ VisError
59
+ There are no virtual experiments or virtuale experiment stats to plot in
60
+ the ExperimentSimulator object. Call 'run_experiments' and 'calc_stats'.
61
+ """
62
+ if trace_opts is None:
63
+ trace_opts = TraceOptsExperiment()
64
+
65
+ if plot_opts is None:
66
+ plot_opts = PlotOptsGeneral()
67
+
68
+ descriptor = exp_sim._sensor_arrays[sens_array_num]._descriptor
69
+ comp_ind = exp_sim._sensor_arrays[sens_array_num].get_field().get_component_index(component)
70
+ samp_time = exp_sim._sensor_arrays[sens_array_num].get_sample_times()
71
+ num_sens = exp_sim._sensor_arrays[sens_array_num].get_measurement_shape()[0]
72
+
73
+ exp_data = exp_sim._exp_data
74
+ exp_stats = exp_sim._exp_stats
75
+
76
+ if exp_data is None or exp_stats is None:
77
+ raise VisError("Before visualising virtual experiment traces the " \
78
+ "virtual experiments must be run. exp_data or exp_stats is None.")
79
+
80
+ if trace_opts.sensors_to_plot is None:
81
+ sensors_to_plot = range(num_sens)
82
+ else:
83
+ sensors_to_plot = trace_opts.sensors_to_plot
84
+
85
+ #---------------------------------------------------------------------------
86
+ # Figure canvas setup
87
+ fig, ax = plt.subplots(figsize=plot_opts.single_fig_size_landscape,
88
+ layout='constrained')
89
+ fig.set_dpi(plot_opts.resolution)
90
+
91
+ #---------------------------------------------------------------------------
92
+ # Plot all simulated experimental points
93
+ if trace_opts.plot_all_exp_points:
94
+ for ss in sensors_to_plot:
95
+ for ee in range(exp_sim._num_exp_per_sim):
96
+ ax.plot(samp_time,
97
+ exp_data[sens_array_num][sim_num,ee,ss,comp_ind,:],
98
+ "+",
99
+ lw=plot_opts.lw,
100
+ ms=plot_opts.ms,
101
+ color=plot_opts.colors[ss % plot_opts.colors_num])
102
+
103
+ sensor_tags = descriptor.create_sensor_tags(num_sens)
104
+ lines = []
105
+ for ss in sensors_to_plot:
106
+ if trace_opts.centre == EExpVisCentre.MEDIAN:
107
+ trace_centre = exp_stats[sens_array_num].med[sim_num,ss,comp_ind,:]
108
+ else:
109
+ trace_centre = exp_stats[sens_array_num].mean[sim_num,ss,comp_ind,:]
110
+
111
+ line, = ax.plot(samp_time,
112
+ trace_centre,
113
+ trace_opts.exp_centre_line,
114
+ label=sensor_tags[ss],
115
+ lw=plot_opts.lw,
116
+ ms=plot_opts.ms,
117
+ color=plot_opts.colors[ss % plot_opts.colors_num])
118
+ lines.append(line)
119
+
120
+ if trace_opts.fill_between is not None:
121
+ upper = np.zeros_like(exp_stats[sens_array_num].min)
122
+ lower = np.zeros_like(exp_stats[sens_array_num].min)
123
+
124
+ if trace_opts.fill_between == EExpVisBounds.MINMAX:
125
+ upper = trace_opts.fill_scale*exp_stats[sens_array_num].min
126
+ lower = trace_opts.fill_scale*exp_stats[sens_array_num].max
127
+ elif trace_opts.fill_between == EExpVisBounds.QUARTILE:
128
+ upper = trace_opts.fill_scale*exp_stats[sens_array_num].q25
129
+ lower = trace_opts.fill_scale*exp_stats[sens_array_num].q75
130
+ elif trace_opts.fill_between == EExpVisBounds.STD:
131
+ upper = trace_centre + \
132
+ trace_opts.fill_scale*exp_stats[sens_array_num].std
133
+ lower = trace_centre - \
134
+ trace_opts.fill_scale*exp_stats[sens_array_num].std
135
+ elif trace_opts.fill_between == EExpVisBounds.MAD:
136
+ upper = trace_centre + \
137
+ trace_opts.fill_scale*exp_stats[sens_array_num].mad
138
+ lower = trace_centre - \
139
+ trace_opts.fill_scale*exp_stats[sens_array_num].mad
140
+
141
+ ax.fill_between(samp_time,
142
+ upper[sim_num,ss,comp_ind,:],
143
+ lower[sim_num,ss,comp_ind,:],
144
+ color=plot_opts.colors[ss % plot_opts.colors_num],
145
+ alpha=0.2)
146
+
147
+ #---------------------------------------------------------------------------
148
+ # Plot simulation and truth line
149
+ if trace_opts.sim_line is not None:
150
+ sim_time = exp_sim._sensor_arrays[sens_array_num].get_field().get_time_steps()
151
+ sim_vals = exp_sim._sensor_arrays[sens_array_num].get_field().sample_field(
152
+ exp_sim._sensor_arrays[sens_array_num]._positions)
153
+
154
+ for ss in sensors_to_plot:
155
+ ax.plot(sim_time,
156
+ sim_vals[ss,comp_ind,:],
157
+ trace_opts.sim_line,
158
+ lw=plot_opts.lw,
159
+ ms=plot_opts.ms)
160
+
161
+ if trace_opts.truth_line is not None:
162
+ truth = exp_sim._sensor_arrays[sens_array_num].get_truth()
163
+ for ss in sensors_to_plot:
164
+ ax.plot(samp_time,
165
+ truth[ss,comp_ind,:],
166
+ trace_opts.truth_line,
167
+ lw=plot_opts.lw,
168
+ ms=plot_opts.ms,
169
+ color=plot_opts.colors[ss % plot_opts.colors_num])
170
+
171
+ #---------------------------------------------------------------------------
172
+ # Axis / legend labels and options
173
+ ax.set_xlabel(trace_opts.time_label,
174
+ fontsize=plot_opts.font_ax_size, fontname=plot_opts.font_name)
175
+ ax.set_ylabel(descriptor.create_label(comp_ind),
176
+ fontsize=plot_opts.font_ax_size, fontname=plot_opts.font_name)
177
+
178
+ if trace_opts.time_min_max is None:
179
+ ax.set_xlim((np.min(samp_time),np.max(samp_time))) # type: ignore
180
+ else:
181
+ ax.set_xlim(trace_opts.time_min_max)
182
+
183
+ if trace_opts.legend_loc is not None:
184
+ ax.legend(handles=lines,
185
+ prop={"size":plot_opts.font_leg_size},
186
+ loc=trace_opts.legend_loc)
187
+
188
+ plt.grid(True)
189
+ plt.draw()
190
+
191
+ return (fig,ax)
@@ -0,0 +1,74 @@
1
+ # ==============================================================================
2
+ # pyvale: the python validation engine
3
+ # License: MIT
4
+ # Copyright (C) 2025 The Computer Aided Validation Team
5
+ # ==============================================================================
6
+
7
+ """
8
+ NOTE: this module is a feature under developement.
9
+ """
10
+
11
+ import numpy as np
12
+ import matplotlib.pyplot as plt
13
+
14
+ class ImageDefDiags:
15
+ @staticmethod
16
+ def plot_speckle_image(image: np.ndarray,
17
+ title: str = "",
18
+ cmap: str = "gray") -> None:
19
+ fig, ax = plt.subplots()
20
+ cset = plt.imshow(image,cmap=plt.get_cmap(cmap),origin='lower')
21
+ ax.set_aspect('equal','box')
22
+ ax.set_title(title,fontsize=12)
23
+ fig.colorbar(cset)
24
+
25
+ @staticmethod
26
+ def plot_vector_image(image: np.ndarray,
27
+ title: str = "",
28
+ cmap: str = "plasma") -> None:
29
+ fig, ax = plt.subplots()
30
+ cset = plt.imshow(image,cmap=plt.get_cmap(cmap),origin='lower')
31
+ ax.set_aspect('equal','box')
32
+ ax.set_title(title,fontsize=12)
33
+ fig.colorbar(cset)
34
+
35
+
36
+ @staticmethod
37
+ def plot_image_xy(image: np.ndarray,
38
+ extent: tuple[float,float,float,float],
39
+ title: str = "",
40
+ cmap: str = "plasma",
41
+ ax_units: str = "mm") -> None:
42
+
43
+ fig, ax = plt.subplots()
44
+ cset = plt.imshow(image,
45
+ aspect='auto',interpolation='none',
46
+ origin='lower',cmap=plt.get_cmap(cmap),
47
+ extent=extent)
48
+ ax.set_aspect('equal','box')
49
+ ax.set_title(title,fontsize=12)
50
+ ax.set_xlabel(f'x [{ax_units}]',fontsize=12)
51
+ ax.set_ylabel(f'y [{ax_units}]',fontsize=12)
52
+ fig.colorbar(cset)
53
+
54
+ @staticmethod
55
+ def plot_all_diags(def_image: np.ndarray,
56
+ def_mask: np.ndarray | None,
57
+ def_image_subpx: np.ndarray,
58
+ subpx_disp_x: np.ndarray,
59
+ subpx_disp_y: np.ndarray,
60
+ subpx_grid_xm: np.ndarray,
61
+ subpx_grid_ym: np.ndarray) -> None:
62
+ image_map = "gray"
63
+ vector_map = "plasma"
64
+
65
+ if def_mask is not None:
66
+ ImageDefDiags.plot_speckle_image('Def. Mask',def_mask,image_map)
67
+
68
+ ImageDefDiags.plot_speckle_image('Subpx Def. Image',def_image_subpx,image_map)
69
+ ImageDefDiags.plot_speckle_image('Def. Image',def_image,image_map)
70
+
71
+ ext = tuple(np.array([subpx_grid_xm.min(),subpx_grid_xm.max(),
72
+ subpx_grid_ym.min(),subpx_grid_ym.max()])*10**3)
73
+ ImageDefDiags.plot_image_xy('Sub Pixel Disp X',subpx_disp_x,ext,vector_map)
74
+ ImageDefDiags.plot_image_xy('Sub Pixel Disp Y',subpx_disp_y,ext,vector_map)
pyvale/visualimages.py ADDED
@@ -0,0 +1,76 @@
1
+ # ==============================================================================
2
+ # pyvale: the python validation engine
3
+ # License: MIT
4
+ # Copyright (C) 2025 The Computer Aided Validation Team
5
+ # ==============================================================================
6
+
7
+ """
8
+ NOTE: this module is a feature under developement.
9
+ """
10
+
11
+ from typing import Any
12
+ import numpy as np
13
+ import matplotlib.pyplot as plt
14
+ from pyvale.camera import CameraBasic2D
15
+ from pyvale.visualopts import PlotOptsGeneral
16
+
17
+ # TODO: this only works for a 2D camera, maybe this should be deprecated
18
+ def plot_measurement_image(camera: CameraBasic2D,
19
+ component: str,
20
+ time_step: int = -1,
21
+ plot_opts: PlotOptsGeneral | None = None
22
+ ) -> tuple[Any,Any]:
23
+
24
+ if plot_opts is None:
25
+ plot_opts = PlotOptsGeneral()
26
+
27
+ comp_ind = camera.get_field().get_component_index(component)
28
+ meas_image = camera.get_measurement_images()[:,:,comp_ind,time_step]
29
+ descriptor = camera.get_descriptor()
30
+
31
+ (fig, ax) = plt.subplots(figsize=plot_opts.single_fig_size_square,
32
+ layout='constrained')
33
+ fig.set_dpi(plot_opts.resolution)
34
+
35
+ cset = plt.imshow(meas_image,
36
+ cmap=plt.get_cmap(plot_opts.cmap_seq),
37
+ origin='lower')
38
+ ax.set_aspect('equal','box')
39
+
40
+ fig.colorbar(cset,
41
+ label=descriptor.create_label_flat(comp_ind))
42
+
43
+ title = f"Time: {camera.get_sample_times()[time_step]}s"
44
+ ax.set_title(title,fontsize=plot_opts.font_head_size)
45
+ ax.set_xlabel(r"x ($px$)",
46
+ fontsize=plot_opts.font_ax_size, fontname=plot_opts.font_name)
47
+ ax.set_ylabel(r"y ($px$)",
48
+ fontsize=plot_opts.font_ax_size, fontname=plot_opts.font_name)
49
+
50
+ return (fig,ax)
51
+
52
+
53
+ def plot_field_image(image: np.ndarray,
54
+ title_str: str | None = None,
55
+ plot_opts: PlotOptsGeneral | None = None
56
+ ) -> tuple[Any,Any]:
57
+
58
+ if plot_opts is None:
59
+ plot_opts = PlotOptsGeneral()
60
+
61
+ (fig, ax) = plt.subplots(figsize=plot_opts.single_fig_size_square,
62
+ layout='constrained')
63
+ fig.set_dpi(plot_opts.resolution)
64
+ cset = plt.imshow(image,
65
+ cmap=plt.get_cmap(plot_opts.cmap_seq))
66
+ #origin='lower')
67
+ ax.set_aspect('equal','box')
68
+ fig.colorbar(cset)
69
+ if title_str is not None:
70
+ ax.set_title(title_str,fontsize=plot_opts.font_head_size)
71
+ ax.set_xlabel(r"x ($px$)",
72
+ fontsize=plot_opts.font_ax_size, fontname=plot_opts.font_name)
73
+ ax.set_ylabel(r"y ($px$)",
74
+ fontsize=plot_opts.font_ax_size, fontname=plot_opts.font_name)
75
+
76
+ return (fig,ax)