1
+ using System ;
2
+ using System . Collections . Generic ;
3
+ using System . IO ;
4
+ using UnityEngine ;
5
+
6
+ namespace UnityVolumeRendering
7
+ {
8
+ /// <summary>
9
+ /// Converts a directory of image slices into a VolumeDataset for volumetric rendering.
10
+ /// </summary>
11
+ public class ImageSequenceImporter : DatasetImporterBase
12
+ {
13
+ private string directoryPath ;
14
+ private string [ ] supportedImageTypes = new string [ ]
15
+ {
16
+ "*.png" ,
17
+ "*.jpg" ,
18
+ } ;
19
+
20
+ public ImageSequenceImporter ( string directoryPath )
21
+ {
22
+ this . directoryPath = directoryPath ;
23
+ }
24
+
25
+ public override VolumeDataset Import ( )
26
+ {
27
+ if ( ! Directory . Exists ( directoryPath ) )
28
+ throw new NullReferenceException ( "No directory found: " + directoryPath ) ;
29
+
30
+ List < string > imagePaths = GetSortedImagePaths ( ) ;
31
+
32
+ if ( ! ImageSetHasUniformDimensions ( imagePaths ) )
33
+ throw new IndexOutOfRangeException ( "Image sequence has non-uniform dimensions" ) ;
34
+
35
+ Vector3Int dimensions = GetVolumeDimensions ( imagePaths ) ;
36
+ int [ ] data = FillSequentialData ( dimensions , imagePaths ) ;
37
+ VolumeDataset dataset = FillVolumeDataset ( data , dimensions ) ;
38
+
39
+ return dataset ;
40
+ }
41
+
42
+ /// <summary>
43
+ /// Gets every file path in the directory with a supported suffix.
44
+ /// </summary>
45
+ /// /// <returns>A sorted list of image file paths.</returns>
46
+ private List < string > GetSortedImagePaths ( )
47
+ {
48
+ var imagePaths = new List < string > ( ) ;
49
+
50
+ foreach ( var type in supportedImageTypes )
51
+ {
52
+ imagePaths . AddRange ( Directory . GetFiles ( directoryPath , type ) ) ;
53
+ }
54
+
55
+ imagePaths . Sort ( ) ;
56
+
57
+ return imagePaths ;
58
+ }
59
+
60
+ /// <summary>
61
+ /// Checks if every image in the set has the same XY dimensions.
62
+ /// </summary>
63
+ /// <param name="imagePaths">The list of image paths to check.</param>
64
+ /// <returns>True if at least one image differs from another.</returns>
65
+ private bool ImageSetHasUniformDimensions ( List < string > imagePaths )
66
+ {
67
+ bool hasUniformDimension = true ;
68
+
69
+ Vector2Int previous , current ;
70
+ previous = GetImageDimensions ( imagePaths [ 0 ] ) ;
71
+
72
+ foreach ( var path in imagePaths )
73
+ {
74
+ current = GetImageDimensions ( path ) ;
75
+
76
+ if ( current . x != previous . x || current . y != previous . y )
77
+ {
78
+ hasUniformDimension = false ;
79
+ break ;
80
+ }
81
+
82
+ previous = current ;
83
+ }
84
+
85
+ return hasUniformDimension ;
86
+ }
87
+
88
+ /// <summary>
89
+ /// Gets the XY dimensions of an image at the path.
90
+ /// </summary>
91
+ /// <param name="path">The image path to check.</param>
92
+ /// <returns>The XY dimensions of the image.</returns>
93
+ private Vector2Int GetImageDimensions ( string path )
94
+ {
95
+ byte [ ] bytes = File . ReadAllBytes ( path ) ;
96
+
97
+ Texture2D texture = new Texture2D ( 1 , 1 ) ;
98
+ texture . LoadImage ( bytes ) ;
99
+
100
+ Vector2Int dimensions = new Vector2Int ( )
101
+ {
102
+ x = texture . width ,
103
+ y = texture . height
104
+ } ;
105
+
106
+ return dimensions ;
107
+ }
108
+
109
+ /// <summary>
110
+ /// Adds a depth value Z to the XY dimensions of the first image.
111
+ /// </summary>
112
+ /// <param name="paths">The set of image paths comprising the volume.</param>
113
+ /// <returns>The dimensions of the volume.</returns>
114
+ private Vector3Int GetVolumeDimensions ( List < string > paths )
115
+ {
116
+ Vector2Int twoDimensional = GetImageDimensions ( paths [ 0 ] ) ;
117
+ Vector3Int threeDimensional = new Vector3Int ( )
118
+ {
119
+ x = twoDimensional . x ,
120
+ y = twoDimensional . y ,
121
+ z = paths . Count
122
+ } ;
123
+ return threeDimensional ;
124
+ }
125
+
126
+ /// <summary>
127
+ /// Converts a volume set of images into a sequential series of values.
128
+ /// </summary>
129
+ /// <param name="dimensions">The XYZ dimensions of the volume.</param>
130
+ /// <param name="paths">The set of image paths comprising the volume.</param>
131
+ /// <returns>The set of sequential values for the volume.</returns>
132
+ private int [ ] FillSequentialData ( Vector3Int dimensions , List < string > paths )
133
+ {
134
+ var data = new List < int > ( dimensions . x * dimensions . y * dimensions . z ) ;
135
+ var texture = new Texture2D ( 1 , 1 ) ;
136
+
137
+ foreach ( var path in paths )
138
+ {
139
+ byte [ ] bytes = File . ReadAllBytes ( path ) ;
140
+ texture . LoadImage ( bytes ) ;
141
+
142
+ Color [ ] pixels = texture . GetPixels ( ) ; // Order priority: X -> Y -> Z
143
+ int [ ] imageData = DensityHelper . ConvertColorsToDensities ( pixels ) ;
144
+
145
+ data . AddRange ( imageData ) ;
146
+ }
147
+
148
+ return data . ToArray ( ) ;
149
+ }
150
+
151
+ /// <summary>
152
+ /// Wraps volume data into a VolumeDataset.
153
+ /// </summary>
154
+ /// <param name="data">Sequential value data for a volume.</param>
155
+ /// <param name="dimensions">The XYZ dimensions of the volume.</param>
156
+ /// <returns>The wrapped volume data.</returns>
157
+ private VolumeDataset FillVolumeDataset ( int [ ] data , Vector3Int dimensions )
158
+ {
159
+ string name = Path . GetFileName ( directoryPath ) ;
160
+
161
+ VolumeDataset dataset = new VolumeDataset ( )
162
+ {
163
+ name = name ,
164
+ datasetName = name ,
165
+ data = data ,
166
+ dimX = dimensions . x ,
167
+ dimY = dimensions . y ,
168
+ dimZ = dimensions . z ,
169
+ scaleX = 1f , // Scale arbitrarily normalised around the x-axis
170
+ scaleY = ( float ) dimensions . y / ( float ) dimensions . x ,
171
+ scaleZ = ( float ) dimensions . z / ( float ) dimensions . x
172
+ } ;
173
+
174
+ return dataset ;
175
+ }
176
+ }
177
+ }
0 commit comments