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