Commit f7a5655a4a0ea49900ba20f7ecef1926b2bf70c5

Authored by tfmoraes
1 parent ff5ac193

ENH: Handle sagittal and coronal images.

* Handling sagittal and coronal (not tested) images. Sagittal and coronal
 images are stored in memmap matrix as axial doing insertion acording to
 orientation;
* Axial, sagittal and coronal are showed in its correspondent plane.
invesalius/control.py
@@ -357,7 +357,7 @@ class Controller(): @@ -357,7 +357,7 @@ class Controller():
357 ps.Publisher().sendMessage('Load slice to viewer', 357 ps.Publisher().sendMessage('Load slice to viewer',
358 (proj.imagedata, 358 (proj.imagedata,
359 proj.mask_dict)) 359 proj.mask_dict))
360 - ps.Publisher().sendMessage('Load slice plane') 360 + #ps.Publisher().sendMessage('Load slice plane')
361 ps.Publisher().sendMessage('Bright and contrast adjustment image',\ 361 ps.Publisher().sendMessage('Bright and contrast adjustment image',\
362 (proj.window, proj.level)) 362 (proj.window, proj.level))
363 ps.Publisher().sendMessage('Update window level value',\ 363 ps.Publisher().sendMessage('Update window level value',\
@@ -409,7 +409,7 @@ class Controller(): @@ -409,7 +409,7 @@ class Controller():
409 name_to_const[dicom.image.orientation_label] 409 name_to_const[dicom.image.orientation_label]
410 proj.window = float(dicom.image.window) 410 proj.window = float(dicom.image.window)
411 proj.level = float(dicom.image.level) 411 proj.level = float(dicom.image.level)
412 - proj.threshold_range = imagedata.GetScalarRange() 412 + proj.threshold_range = (-1024, 3033)
413 413
414 414
415 ###### 415 ######
@@ -445,14 +445,17 @@ class Controller(): @@ -445,14 +445,17 @@ class Controller():
445 bits = dicom.image.bits_allocad 445 bits = dicom.image.bits_allocad
446 sop_class_uid = dicom.acquisition.sop_class_uid 446 sop_class_uid = dicom.acquisition.sop_class_uid
447 xyspacing = dicom.image.spacing 447 xyspacing = dicom.image.spacing
  448 + orientation = dicom.image.orientation_label
448 449
449 if sop_class_uid == '1.2.840.10008.5.1.4.1.1.7': #Secondary Capture Image Storage 450 if sop_class_uid == '1.2.840.10008.5.1.4.1.1.7': #Secondary Capture Image Storage
450 use_dcmspacing = 1 451 use_dcmspacing = 1
451 else: 452 else:
452 use_dcmspacing = 0 453 use_dcmspacing = 0
453 454
454 - imagedata = utils.CreateImageData(filelist, zspacing, xyspacing,size,  
455 - bits, use_dcmspacing) 455 + #imagedata = utils.CreateImageData(filelist, zspacing, xyspacing,size,
  456 + #bits, use_dcmspacing)
  457 +
  458 + imagedata = None
456 459
457 # 1(a): Fix gantry tilt, if any 460 # 1(a): Fix gantry tilt, if any
458 tilt_value = dicom.acquisition.tilt 461 tilt_value = dicom.acquisition.tilt
@@ -466,10 +469,23 @@ class Controller(): @@ -466,10 +469,23 @@ class Controller():
466 tilt_value = -1*tilt_value 469 tilt_value = -1*tilt_value
467 imagedata = utils.FixGantryTilt(imagedata, tilt_value) 470 imagedata = utils.FixGantryTilt(imagedata, tilt_value)
468 471
469 - self.matrix, self.filename = utils.dcm2memmap(filelist, size) 472 + wl = float(dicom.image.level)
  473 + ww = float(dicom.image.window)
  474 +
  475 + self.matrix, self.filename = utils.dcm2memmap(filelist, size,
  476 + orientation)
470 self.Slice = sl.Slice() 477 self.Slice = sl.Slice()
471 self.Slice.matrix = self.matrix 478 self.Slice.matrix = self.matrix
472 - self.Slice.spacing = xyspacing[0], xyspacing[1], zspacing 479 +
  480 + if orientation == 'AXIAL':
  481 + self.Slice.spacing = xyspacing[0], xyspacing[1], zspacing
  482 + elif orientation == 'CORONAL':
  483 + self.Slice.spacing = xyspacing[0], zspacing, xyspacing[1]
  484 + elif orientation == 'SAGITTAL':
  485 + self.Slice.spacing = zspacing, xyspacing[1], xyspacing[0]
  486 +
  487 + self.Slice.window_level = wl
  488 + self.Slice.window_width = ww
473 return imagedata, dicom 489 return imagedata, dicom
474 490
475 def LoadImagedataInfo(self): 491 def LoadImagedataInfo(self):
invesalius/data/imagedata_utils.py
@@ -456,13 +456,19 @@ class ImageCreator: @@ -456,13 +456,19 @@ class ImageCreator:
456 456
457 return imagedata 457 return imagedata
458 458
459 -def dcm2memmap(files, slice_size): 459 +def dcm2memmap(files, slice_size, orientation):
460 """ 460 """
461 From a list of dicom files it creates memmap file in the temp folder and 461 From a list of dicom files it creates memmap file in the temp folder and
462 returns it and its related filename. 462 returns it and its related filename.
463 """ 463 """
464 temp_file = tempfile.mktemp() 464 temp_file = tempfile.mktemp()
465 - shape = len(files), slice_size[1], slice_size[0] 465 +
  466 + if orientation == 'SAGITTAL':
  467 + shape = slice_size[0], slice_size[1], len(files)
  468 + elif orientation == 'CORONAL':
  469 + shape = slice_size[1], len(files), slice_size[0]
  470 + else:
  471 + shape = len(files), slice_size[1], slice_size[0]
466 472
467 matrix = numpy.memmap(temp_file, mode='w+', dtype='int16', shape=shape) 473 matrix = numpy.memmap(temp_file, mode='w+', dtype='int16', shape=shape)
468 dcm_reader = vtkgdcm.vtkGDCMImageReader() 474 dcm_reader = vtkgdcm.vtkGDCMImageReader()
@@ -471,25 +477,43 @@ def dcm2memmap(files, slice_size): @@ -471,25 +477,43 @@ def dcm2memmap(files, slice_size):
471 dcm_reader.Update() 477 dcm_reader.Update()
472 image = dcm_reader.GetOutput() 478 image = dcm_reader.GetOutput()
473 array = numpy_support.vtk_to_numpy(image.GetPointData().GetScalars()) 479 array = numpy_support.vtk_to_numpy(image.GetPointData().GetScalars())
474 - array.shape = matrix.shape[1], matrix.shape[2]  
475 - matrix[n] = array 480 + if orientation == 'CORONAL':
  481 + array.shape = matrix.shape[0], matrix.shape[2]
  482 + matrix[:, n, :] = array
  483 + elif orientation == 'SAGITTAL':
  484 + array.shape = matrix.shape[0], matrix.shape[1]
  485 + matrix[:, :, n] = array
  486 + else:
  487 + array.shape = matrix.shape[1], matrix.shape[2]
  488 + matrix[n] = array
476 489
477 matrix.flush() 490 matrix.flush()
478 return matrix, temp_file 491 return matrix, temp_file
479 492
480 -def to_vtk(n_array, spacing): 493 +def to_vtk(n_array, spacing, slice_number, orientation):
481 dy, dx = n_array.shape 494 dy, dx = n_array.shape
482 n_array.shape = dx * dy 495 n_array.shape = dx * dy
483 496
484 v_image = numpy_support.numpy_to_vtk(n_array) 497 v_image = numpy_support.numpy_to_vtk(n_array)
485 498
  499 + print orientation
  500 + if orientation == 'AXIAL':
  501 + dz = 1
  502 + extent = (0, dx -1, 0, dy -1, slice_number, slice_number)
  503 + elif orientation == 'SAGITAL':
  504 + dx, dy, dz = 1, dx, dy
  505 + extent = (slice_number, slice_number, 0, dy - 1, 0, dz - 1)
  506 + elif orientation == 'CORONAL':
  507 + dx, dy, dz = dx, 1, dy
  508 + extent = (0, dx - 1, slice_number, slice_number, 0, dz - 1)
  509 +
486 # Generating the vtkImageData 510 # Generating the vtkImageData
487 image = vtk.vtkImageData() 511 image = vtk.vtkImageData()
488 - image.SetDimensions(dx, dy, 1)  
489 image.SetOrigin(0, 0, 0) 512 image.SetOrigin(0, 0, 0)
490 image.SetSpacing(spacing) 513 image.SetSpacing(spacing)
491 image.SetNumberOfScalarComponents(1) 514 image.SetNumberOfScalarComponents(1)
492 - image.SetExtent(0, dx -1, 0, dy -1, 0, 0) 515 + image.SetDimensions(dx, dy, dz)
  516 + image.SetExtent(extent)
493 image.SetScalarType(numpy_support.get_vtk_array_type(n_array.dtype)) 517 image.SetScalarType(numpy_support.get_vtk_array_type(n_array.dtype))
494 image.AllocateScalars() 518 image.AllocateScalars()
495 image.GetPointData().SetScalars(v_image) 519 image.GetPointData().SetScalars(v_image)
invesalius/data/slice_.py
@@ -239,15 +239,11 @@ class Slice(object): @@ -239,15 +239,11 @@ class Slice(object):
239 def GetSlices(self, orientation, slice_number): 239 def GetSlices(self, orientation, slice_number):
240 if orientation == 'AXIAL': 240 if orientation == 'AXIAL':
241 n_array = numpy.array(self.matrix[slice_number]) 241 n_array = numpy.array(self.matrix[slice_number])
242 - spacing = self.spacing  
243 elif orientation == 'CORONAL': 242 elif orientation == 'CORONAL':
244 n_array = numpy.array(self.matrix[..., slice_number, ...]) 243 n_array = numpy.array(self.matrix[..., slice_number, ...])
245 - spacing = self.spacing[0], self.spacing[2], self.spacing[1]  
246 elif orientation == 'SAGITAL': 244 elif orientation == 'SAGITAL':
247 n_array = numpy.array(self.matrix[..., ..., slice_number]) 245 n_array = numpy.array(self.matrix[..., ..., slice_number])
248 - spacing = self.spacing[1], self.spacing[2], self.spacing[0]  
249 -  
250 - image = iu.to_vtk(n_array, spacing) 246 + image = iu.to_vtk(n_array, self.spacing, slice_number, orientation)
251 image = self.do_ww_wl(image) 247 image = self.do_ww_wl(image)
252 return image 248 return image
253 249
@@ -619,11 +615,13 @@ class Slice(object): @@ -619,11 +615,13 @@ class Slice(object):
619 ps.Publisher().sendMessage('Update slice viewer') 615 ps.Publisher().sendMessage('Update slice viewer')
620 616
621 def do_ww_wl(self, image): 617 def do_ww_wl(self, image):
  618 + print self.window_width, self.window_level
  619 + print image.GetScalarRange()
622 colorer = vtk.vtkImageMapToWindowLevelColors() 620 colorer = vtk.vtkImageMapToWindowLevelColors()
623 colorer.SetInput(image) 621 colorer.SetInput(image)
624 - colorer.SetWindow(255)  
625 - colorer.SetLevel(127)  
626 - colorer.SetOutputFormatToRGBA() 622 + colorer.SetWindow(self.window_width)
  623 + colorer.SetLevel(self.window_level)
  624 + colorer.SetOutputFormatToRGB()
627 colorer.Update() 625 colorer.Update()
628 626
629 return colorer.GetOutput() 627 return colorer.GetOutput()
invesalius/data/viewer_slice.py
@@ -173,7 +173,6 @@ class Viewer(wx.Panel): @@ -173,7 +173,6 @@ class Viewer(wx.Panel):
173 self.cam = ren.GetActiveCamera() 173 self.cam = ren.GetActiveCamera()
174 self.ren = ren 174 self.ren = ren
175 175
176 -  
177 def SetInteractorStyle(self, state): 176 def SetInteractorStyle(self, state):
178 self.state = state 177 self.state = state
179 action = {const.SLICE_STATE_CROSS: 178 action = {const.SLICE_STATE_CROSS:
@@ -1056,26 +1055,26 @@ class Viewer(wx.Panel): @@ -1056,26 +1055,26 @@ class Viewer(wx.Panel):
1056 return cursor 1055 return cursor
1057 1056
1058 def SetInput(self, imagedata, mask_dict): 1057 def SetInput(self, imagedata, mask_dict):
1059 - pass  
1060 - #self.imagedata = imagedata 1058 + self.slice_ = sl.Slice()
1061 1059
1062 - ##ren = self.ren  
1063 - #interactor = self.interactor 1060 + max_slice_number = sl.Slice().GetNumberOfSlices(self.orientation)
  1061 + self.scroll.SetScrollbar(wx.SB_VERTICAL, 1, max_slice_number,
  1062 + max_slice_number)
1064 1063
1065 - ## Slice pipeline, to be inserted into current viewer  
1066 - #slice_ = sl.Slice() 1064 + self.actor = vtk.vtkImageActor()
  1065 + self.ren.AddActor(self.actor)
  1066 + self.set_slice_number(0)
  1067 + self.__update_camera()
  1068 + self.ren.ResetCamera()
1067 #if slice_.imagedata is None: 1069 #if slice_.imagedata is None:
1068 #slice_.SetInput(imagedata, mask_dict) 1070 #slice_.SetInput(imagedata, mask_dict)
1069 1071
1070 - actor = vtk.vtkImageActor()  
1071 ##actor.SetInput(slice_.GetOutput()) 1072 ##actor.SetInput(slice_.GetOutput())
1072 #self.LoadRenderers(slice_.GetOutput()) 1073 #self.LoadRenderers(slice_.GetOutput())
1073 #self.__configure_renderers() 1074 #self.__configure_renderers()
1074 #ren = self.slice_data_list[0].renderer 1075 #ren = self.slice_data_list[0].renderer
1075 #actor = self.slice_data_list[0].actor 1076 #actor = self.slice_data_list[0].actor
1076 #actor_bound = actor.GetBounds() 1077 #actor_bound = actor.GetBounds()
1077 - self.actor = actor  
1078 - self.ren.AddActor(self.actor)  
1079 #self.cam = ren.GetActiveCamera() 1078 #self.cam = ren.GetActiveCamera()
1080 1079
1081 #for slice_data in self.slice_data_list: 1080 #for slice_data in self.slice_data_list:
@@ -1088,12 +1087,10 @@ class Viewer(wx.Panel): @@ -1088,12 +1087,10 @@ class Viewer(wx.Panel):
1088 1087
1089 #if actor.GetSliceNumberMax() % number_of_slices: 1088 #if actor.GetSliceNumberMax() % number_of_slices:
1090 #max_slice_number += 1 1089 #max_slice_number += 1
1091 - max_slice_number = sl.Slice().GetNumberOfSlices(self.orientation)  
1092 - self.scroll.SetScrollbar(wx.SB_VERTICAL, 1, max_slice_number,  
1093 - max_slice_number)  
1094 #self.set_scroll_position(0) 1090 #self.set_scroll_position(0)
1095 1091
1096 #actor_bound = actor.GetBounds() 1092 #actor_bound = actor.GetBounds()
  1093 + self.interactor.Render()
1097 1094
1098 #self.EnableText() 1095 #self.EnableText()
1099 ## Insert cursor 1096 ## Insert cursor
@@ -1268,43 +1265,23 @@ class Viewer(wx.Panel): @@ -1268,43 +1265,23 @@ class Viewer(wx.Panel):
1268 renderer.AddViewProp(slice_data.box_actor) 1265 renderer.AddViewProp(slice_data.box_actor)
1269 return slice_data 1266 return slice_data
1270 1267
1271 - def __update_camera(self, slice_data): 1268 + def __update_camera(self):
1272 orientation = self.orientation 1269 orientation = self.orientation
1273 proj = project.Project() 1270 proj = project.Project()
1274 - orig_orien = proj.original_orientation 1271 + orig_orien = 1 #proj.original_orientation
1275 1272
1276 - cam = slice_data.renderer.GetActiveCamera()  
1277 - cam.SetFocalPoint(0, 0, 0)  
1278 - cam.SetViewUp(const.SLICE_POSITION[orig_orien][0][self.orientation])  
1279 - cam.SetPosition(const.SLICE_POSITION[orig_orien][1][self.orientation])  
1280 - cam.ComputeViewPlaneNormal()  
1281 - cam.OrthogonalizeViewUp()  
1282 - cam.ParallelProjectionOn()  
1283 -  
1284 - self.__update_display_extent(slice_data) 1273 + self.cam.SetFocalPoint(0, 0, 0)
  1274 + self.cam.SetViewUp(const.SLICE_POSITION[orig_orien][0][self.orientation])
  1275 + self.cam.SetPosition(const.SLICE_POSITION[orig_orien][1][self.orientation])
  1276 + self.cam.ComputeViewPlaneNormal()
  1277 + self.cam.OrthogonalizeViewUp()
  1278 + self.cam.ParallelProjectionOn()
1285 1279
1286 - slice_data.renderer.ResetCamera()  
1287 #slice_data.renderer.Render() 1280 #slice_data.renderer.Render()
1288 1281
1289 - def __update_display_extent(self, slice_data):  
1290 - e = self.imagedata.GetWholeExtent()  
1291 - proj = project.Project()  
1292 -  
1293 - pos = slice_data.number  
1294 -  
1295 - x = (pos, pos, e[2], e[3], e[4], e[5])  
1296 - y = (e[0], e[1], pos, pos, e[4], e[5])  
1297 - z = (e[0], e[1], e[2], e[3], pos, pos)  
1298 -  
1299 - if (proj.original_orientation == const.AXIAL):  
1300 - new_extent = {"SAGITAL": x, "CORONAL": y, "AXIAL": z}  
1301 - elif(proj.original_orientation == const.SAGITAL):  
1302 - new_extent = {"SAGITAL": z,"CORONAL": x,"AXIAL": y}  
1303 - elif(proj.original_orientation == const.CORONAL):  
1304 - new_extent = {"SAGITAL": x,"CORONAL": z,"AXIAL": y}  
1305 -  
1306 - slice_data.actor.SetDisplayExtent(new_extent[self.orientation])  
1307 - slice_data.renderer.ResetCameraClippingRange() 1282 + def __update_display_extent(self, image):
  1283 + self.actor.SetDisplayExtent(image.GetExtent())
  1284 + self.ren.ResetCameraClippingRange()
1308 1285
1309 def UpdateRender(self, evt): 1286 def UpdateRender(self, evt):
1310 self.interactor.Render() 1287 self.interactor.Render()
@@ -1341,18 +1318,10 @@ class Viewer(wx.Panel): @@ -1341,18 +1318,10 @@ class Viewer(wx.Panel):
1341 1318
1342 def OnScrollBar(self, evt=None): 1319 def OnScrollBar(self, evt=None):
1343 pos = self.scroll.GetThumbPosition() 1320 pos = self.scroll.GetThumbPosition()
1344 - slice_ = sl.Slice()  
1345 - image = slice_.GetSlices(self.orientation, pos)  
1346 - self.actor.SetInput(image)  
1347 - self.ren.ResetCamera() 1321 + self.set_slice_number(pos)
1348 self.interactor.Render() 1322 self.interactor.Render()
1349 - print "slice", pos  
1350 - #self.set_slice_number(pos)  
1351 - ##self.UpdateSlice3D(pos)  
1352 - #self.pos = pos  
1353 - #self.interactor.Render()  
1354 - #if evt:  
1355 - #evt.Skip() 1323 + if evt:
  1324 + evt.Skip()
1356 1325
1357 def OnScrollBarRelease(self, evt): 1326 def OnScrollBarRelease(self, evt):
1358 #self.UpdateSlice3D(self.pos) 1327 #self.UpdateSlice3D(self.pos)
@@ -1409,43 +1378,14 @@ class Viewer(wx.Panel): @@ -1409,43 +1378,14 @@ class Viewer(wx.Panel):
1409 evt.Skip() 1378 evt.Skip()
1410 1379
1411 def set_slice_number(self, index): 1380 def set_slice_number(self, index):
1412 - self.slice_number = index  
1413 - # for m in self.actors_by_slice_number.values():  
1414 - # for actor in m:  
1415 - # actor.SetVisibility(0)  
1416 - # Removing actor from the previous renderers/slice.  
1417 - for n in self.renderers_by_slice_number:  
1418 - renderer = self.renderers_by_slice_number[n]  
1419 - for actor in self.actors_by_slice_number.get(n, []):  
1420 - renderer.RemoveActor(actor)  
1421 -  
1422 - self.renderers_by_slice_number = {}  
1423 -  
1424 - for n, slice_data in enumerate(self.slice_data_list):  
1425 - ren = slice_data.renderer  
1426 - actor = slice_data.actor  
1427 - pos = self.layout[0] * self.layout[1] * index + n  
1428 - max = actor.GetSliceNumberMax() + 1  
1429 - if pos < max:  
1430 - self.renderers_by_slice_number[pos] = ren  
1431 - for m_actor in self.actors_by_slice_number.get(pos, []):  
1432 - ren.AddActor(m_actor)  
1433 - slice_data.SetNumber(pos)  
1434 - # for actor in self.actors_by_slice_number.get(pos, []):  
1435 - # actor.SetVisibility(1)  
1436 - self.__update_display_extent(slice_data)  
1437 - slice_data.Show()  
1438 - else:  
1439 - slice_data.Hide()  
1440 -  
1441 - position = {"SAGITAL": {0: slice_data.number},  
1442 - "CORONAL": {1: slice_data.number},  
1443 - "AXIAL": {2: slice_data.number}}  
1444 -  
1445 - #if 'DEFAULT' in self.modes:  
1446 - # ps.Publisher().sendMessage(  
1447 - # 'Update cursor single position in slice',  
1448 - # position[self.orientation]) 1381 + image = self.slice_.GetSlices(self.orientation, index)
  1382 + self.actor.SetInput(image)
  1383 + self.__update_display_extent(image)
  1384 + print "slice", index
  1385 + print "display extent", self.actor.GetDisplayExtent()
  1386 + print "whole extent", image.GetWholeExtent()
  1387 + print "boundsa", self.actor.GetBounds()
  1388 + print "camera", self.cam.GetPosition(), self.cam.GetFocalPoint()
1449 1389
1450 def ChangeSliceNumber(self, pubsub_evt): 1390 def ChangeSliceNumber(self, pubsub_evt):
1451 index = pubsub_evt.data 1391 index = pubsub_evt.data