IMHOTEP Framework
 All Classes Namespaces Functions Variables Enumerations Enumerator Properties Pages
DICOM3D.cs
1 using System;
2 using System.Runtime.InteropServices;
3 using UnityEngine;
4 using itk.simple;
5 using System.Collections.Generic;
6 
7 
8 public class DICOM3D : DICOM
9 {
11  public Color32[] colors;
12 
13  private Texture3D texture3D;
14 
15  public Bounds boundingBox { private set; get; }
16 
17  public Histogram histogram;
18 
19  public DICOM3D ( DICOMSeries seriesInfo ) : base( seriesInfo )
20  {
21  dimensions = 3;
22 
23  Image firstSlice = seriesInfo.firstSlice;
24  Image lastSlice = seriesInfo.lastSlice;
25 
26  VectorDouble o1 = firstSlice.GetOrigin();
27  if ( o1.Count < 3) {
28  throw( new System.Exception ("Invalid origins found in first image."));
29  }
30  origin = new Vector3 ((float)o1 [0], (float)o1 [1], (float)o1 [2]);
31 
32  // Load the pixel spacing:
33  // NOTE: It seems that the the first value is the spacing between rows (i.e. y direction),
34  // the second value is the spacing between columns (i.e. x direction).
35  // I was not able to verify this so far, since all test dicoms we had have the same spacing in
36  // x and y direction...
37  VectorDouble spacing = firstSlice.GetSpacing();
38  if( spacing.Count < 2 )
39  throw( new System.Exception ("Invalid pixel spacing found in images."));
40  pixelSpacing = new Vector2 ((float)spacing [1], (float)spacing [0] );
41 
42  // DICOMSeries already calculated these values, so get them from there:
43  directionCosineX = seriesInfo.directionCosineX;
44  directionCosineY = seriesInfo.directionCosineY;
45 
46  // Generate the transformation matrices which can later be used to translate pixels to
47  // 3D positions and vice versa.
49 
50  loadVolumeData ();
51 
52 
53  Vector3 corner1 = transformPixelToPatientPos (Vector2.zero, 0f);
54  Vector2 imgDimensions = new Vector2 (firstSlice.GetWidth (), firstSlice.GetHeight ());
55  Vector3 corner2 = transformPixelToPatientPos (imgDimensions, seriesInfo.numberOfSlices-1);
56  //boundingBox = new Bounds ((max - min) / 2 + min, (max - min));
57  boundingBox = new Bounds ((corner2 - corner1) / 2 + corner1, (corner2 - corner1));
58  Debug.Log ("bounding Box: " + boundingBox.center + " " + boundingBox.size);
59  }
60 
64  private void loadVolumeData()
65  {
66  // Get all file names for the series:
67  VectorString fileNames = seriesInfo.filenames;
68 
69  // Create a reader which will read the whole series:
70  ImageSeriesReader reader = new ImageSeriesReader ();
71  reader.SetFileNames (fileNames);
72  // Load the entire image into a series:
73  Image image = reader.Execute();
74 
75  origTexWidth = (int)image.GetWidth ();
76  origTexHeight = (int)image.GetHeight ();
77  origTexDepth = (int)image.GetDepth ();
78  texWidth = Mathf.NextPowerOfTwo ((int)image.GetWidth ());
79  texHeight = Mathf.NextPowerOfTwo ((int)image.GetHeight ());
80  texDepth = Mathf.NextPowerOfTwo ((int)image.GetDepth ());
81  texPaddingWidth = texWidth - origTexWidth;
82  texPaddingHeight = texHeight - origTexHeight;
83  texPaddingDepth = texDepth - origTexDepth;
84  Debug.Log ("Original texture dimensions: " + origTexWidth + " " + origTexHeight + " " + origTexDepth);
85  Debug.Log ("Texture dimensions: " + texWidth + " " + texHeight + " " + texDepth);
86  colors = new Color32[ texWidth * texHeight * texDepth ];
87 
88  int intercept = 0;
89  int slope = 1;
90  try {
91  intercept = Int32.Parse( image.GetMetaData("0028|1052") );
92  slope = Int32.Parse( image.GetMetaData("0028|1053") );
93  } catch {
94  }
95  Debug.Log ("Slope: " + slope + " Intercept: " + intercept);
96 
97  if (image.GetDimension () != 3)
98  {
99  throw( new System.Exception( "Cannot load volume: Image needs to be 3D. Dimensions of image: " + image.GetDimension()));
100  }
101 
102  histogram = new Histogram ();
103 
104  UInt32 min = UInt32.MaxValue;
105  UInt32 max = UInt32.MinValue;
106 
107  Debug.Log ("Pixel format: " + image.GetPixelID ());
108 
109  // Copy the image into a colors array:
110  if (image.GetPixelID () == PixelIDValueEnum.sitkUInt16) {
111  IntPtr bufferPtr = image.GetBufferAsUInt16 ();
112 
113  unsafe {
114  UInt16 *ptr = (UInt16 *)bufferPtr.ToPointer();
115 
116  int consecutiveIndex = 0;
117  for (UInt32 z = 0; z < texDepth; z++) {
118  for (UInt32 y = 0; y < texHeight; y++) {
119  for (UInt32 x = 0; x < texWidth; x++) {
120  if (x < origTexWidth && y < origTexHeight && z < origTexDepth ) {
121  long jumpingIndex = x + y * texWidth + z*texWidth*texHeight;
122 
123  UInt32 pixelValue = (UInt32)((UInt16)ptr [consecutiveIndex]);
124  colors [jumpingIndex] = F2C (pixelValue);
125 
126  if (pixelValue > max)
127  max = pixelValue;
128  if (pixelValue < min)
129  min = pixelValue;
130 
131  histogram.addValue (pixelValue);
132 
133  consecutiveIndex++;
134  }
135  }
136  }
137  }
138  }
139  } else if ( image.GetPixelID() == PixelIDValueEnum.sitkInt16 ) {
140  IntPtr bufferPtr = image.GetBufferAsInt16 ();
141 
142  unsafe {
143  Int16 *ptr = (Int16 *)bufferPtr.ToPointer();
144 
145  int consecutiveIndex = 0;
146  for (UInt32 z = 0; z < texDepth; z++) {
147  for (UInt32 y = 0; y < texHeight; y++) {
148  for (UInt32 x = 0; x < texWidth; x++) {
149  if (x < origTexWidth && y < origTexHeight && z < origTexDepth ) {
150  long jumpingIndex = x + y * texWidth + z*texWidth*texHeight;
151 
152  UInt32 pixelValue = (UInt32)((Int16)ptr[consecutiveIndex] + Int16.MaxValue);
153  colors [jumpingIndex] = F2C (pixelValue);
154 
155  if (pixelValue > max)
156  max = pixelValue;
157  if (pixelValue < min)
158  min = pixelValue;
159 
160  histogram.addValue (pixelValue);
161 
162  consecutiveIndex++;
163  }
164  }
165  }
166  }
167  }
168  } else if ( image.GetPixelID() == PixelIDValueEnum.sitkInt32 ) {
169  IntPtr bufferPtr = image.GetBufferAsInt32 ();
170 
171  unsafe {
172  Int32 *ptr = (Int32 *)bufferPtr.ToPointer();
173 
174  int consecutiveIndex = 0;
175 
176  for (UInt32 z = 0; z < texDepth; z++) {
177  for (UInt32 y = 0; y < texHeight; y++) {
178  for (UInt32 x = 0; x < texWidth; x++) {
179  if (x < origTexWidth && y < origTexHeight && z < origTexDepth ) {
180  long jumpingIndex = x + y * texWidth + z*texWidth*texHeight;
181 
182  // TODO: To move from Int32 to UInt32 range, we should add Int32.MaxValue?!
183  // However, when we do this,
184  UInt32 pixelValue = (UInt32)((Int32)ptr[consecutiveIndex]) + (UInt32)Int16.MaxValue;
185  colors [jumpingIndex] = F2C (pixelValue);
186 
187  if (pixelValue > max)
188  max = pixelValue;
189  if (pixelValue < min)
190  min = pixelValue;
191 
192  histogram.addValue (pixelValue);
193 
194  consecutiveIndex++;
195  }
196  }
197  }
198  }
199  }
200  } else {
201  throw(new System.Exception ("Unsupported pixel format: " + image.GetPixelID()));
202  }
203  /*IntPtr bufferPtr;
204  UInt32 numberOfPixels = image.GetWidth () * image.GetHeight () * image.GetDepth();
205  if (image.GetPixelID () == PixelIDValueEnum.sitkUInt16) {
206  bufferPtr = image.GetBufferAsUInt16 ();
207 
208  UInt16[] colorsTmp = new UInt16[ numberOfPixels ];
209  Int16[] tmp = new Int16[ numberOfPixels ];
210  Marshal.Copy( bufferPtr, tmp, 0, (int)numberOfPixels );
211  System.Buffer.BlockCopy (tmp, 0, colorsTmp, 0, (int)numberOfPixels);
212 
213  int index = 0;
214  //for (UInt32 z = 0; z < texDepth; z++) {
215  for (UInt32 z = 0; z < texDepth; z++) {
216  for (UInt32 y = 0; y < texHeight; y++) {
217  for (UInt32 x = 0; x < texWidth; x++) {
218  //long consecutiveIndex = (texWidth-1-x) + y*texWidth + z*texWidth*texHeight;
219  long consecutiveIndex = x + y * texWidth + z*texWidth*texHeight;
220  if (x < origTexWidth && y < origTexHeight && z < origTexDepth) {
221  UInt16 pixelValue = (UInt16)((colorsTmp [index] - intercept) / slope);
222  colors [consecutiveIndex] = F2C (pixelValue);
223 
224  if (pixelValue > max)
225  max = pixelValue;
226  if (pixelValue < min)
227  min = pixelValue;
228 
229  histogram.addValue (pixelValue);
230 
231  index++;
232  }
233  }
234  }
235  }
236  } else if ( image.GetPixelID() == PixelIDValueEnum.sitkInt16 ) {
237  bufferPtr = image.GetBufferAsInt16 ();
238 
239  Int16[] colorsTmp = new Int16[ numberOfPixels ];
240  Marshal.Copy( bufferPtr, colorsTmp, 0, (int)numberOfPixels );
241 
242  int index = 0;
243  //for (UInt32 z = 0; z < texDepth; z++) {
244  for (UInt32 z = 0; z < texDepth; z++) {
245  for (UInt32 y = 0; y < texHeight; y++) {
246  for (UInt32 x = 0; x < texWidth; x++) {
247  //long consecutiveIndex = (texWidth-1-x) + y*texWidth + z*texWidth*texHeight;
248  long consecutiveIndex = x + y * texWidth + z*texWidth*texHeight;
249  if (x < origTexWidth && y < origTexHeight && z < origTexDepth )
250  {
251  //Int16 pixelValueInt16 = (Int16)((colorsTmp [index] - intercept) / slope);
252  //UInt32 pixelValue = (UInt32)((int)pixelValueInt16 + 32768);
253 
254  UInt16 pixelValue = (UInt16)((colorsTmp [index] - intercept) / slope);
255  colors [ consecutiveIndex] = F2C(pixelValue);
256 
257  if (pixelValue > max)
258  max = pixelValue;
259  if (pixelValue < min)
260  min = pixelValue;
261 
262  histogram.addValue (pixelValue);
263 
264  index++;
265  }
266  }
267  }
268  }
269  } else if ( image.GetPixelID() == PixelIDValueEnum.sitkInt32 ) {
270  bufferPtr = image.GetBufferAsInt32 ();
271 
272  Int32[] colorsTmp = new Int32[ numberOfPixels ];
273  Marshal.Copy( bufferPtr, colorsTmp, 0, (int)numberOfPixels );
274 
275  int index = 0;
276  //for (UInt32 z = 0; z < texDepth; z++) {
277  for (UInt32 z = 0; z < texDepth; z++) {
278  for (UInt32 y = 0; y < texHeight; y++) {
279  for (UInt32 x = 0; x < texWidth; x++) {
280  //long consecutiveIndex = (texWidth-1-x) + y*texWidth + z*texWidth*texHeight;
281  long consecutiveIndex = x + y * texWidth + z*texWidth*texHeight;
282  if (x < origTexWidth && y < origTexHeight && z < origTexDepth) {
283  Int32 pixelValueInt32 = (Int32)((colorsTmp [index] - intercept) / slope);
284 
285  UInt32 pixelValue = (UInt32)((Int64)pixelValueInt32 + Int32.MaxValue);
286  colors [ consecutiveIndex ] = F2C(pixelValue);
287 
288  if (pixelValue > max)
289  max = pixelValue;
290  if (pixelValue < min)
291  min = pixelValue;
292 
293  histogram.addValue (pixelValue);
294 
295  index++;
296  }
297  }
298  }
299  }
300  } else {
301  throw(new System.Exception ("Unsupported pixel format: " + image.GetPixelID()));
302  }*/
303 
304  // Manually set the min and max values, because we just caculated them for the whole volume and
305  // can thus be sure that we have the correct values:
306  seriesInfo.setMinMaxPixelValues (min, max);
307 
308 
309  histogram.setMinMaxPixelValues (min, max);
310 
311  // Make the loaded image accessable from elsewhere:
312  this.image = image;
313 
314 
315  Debug.Log ("Loaded.");
316  }
317 
318  public Texture3D getTexture3D()
319  {
320  if( texture3D != null )
321  return texture3D;
322 
323 
324 
325  Debug.Log ("Generating DICOM texture:");
326  texture3D = new Texture3D( texWidth, texHeight, texDepth, TextureFormat.ARGB32, false);
327  texture3D.SetPixels32(colors); //needs around 0.15 sec for a small DICOM, TODO coroutine?
328  texture3D.Apply();
329  texture3D.wrapMode = TextureWrapMode.Clamp;
330 
331  Debug.Log ("\tGenerated DICOM texture.");
332  return texture3D;
333  }
334 }
335 
int texPaddingDepth
Definition: DICOM.cs:35
Vector2 pixelSpacing
Definition: DICOM.cs:47
Vector3 directionCosineY
Definition: DICOM.cs:63
Vector3 directionCosineX
Definition: DICOM.cs:57
Vector3 origin
Definition: DICOM.cs:45
int origTexDepth
Definition: DICOM.cs:28
int numberOfSlices
Definition: DICOMSeries.cs:23
int origTexWidth
Definition: DICOM.cs:24
Image image
Definition: DICOM.cs:42
Definition: DICOM.cs:8
int texPaddingWidth
Definition: DICOM.cs:31
int texPaddingHeight
Definition: DICOM.cs:33
int origTexHeight
Definition: DICOM.cs:26
void setupTransformationMatrices()
Definition: DICOM.cs:98
static Color32 F2C(UInt32 value)
Definition: DICOM.cs:173
int dimensions
Definition: DICOM.cs:18
Color32[] colors
Definition: DICOM3D.cs:11
Vector3 transformPixelToPatientPos(Vector2 pixel, float layer=0)
Definition: DICOM.cs:138
DICOMSeries seriesInfo
Definition: DICOM.cs:13