Commit f7a5655a4a0ea49900ba20f7ecef1926b2bf70c5
1 parent
ff5ac193
Exists in
master
and in
67 other branches
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.
Showing
4 changed files
with
91 additions
and
113 deletions
Show diff stats
invesalius/control.py
| ... | ... | @@ -357,7 +357,7 @@ class Controller(): |
| 357 | 357 | ps.Publisher().sendMessage('Load slice to viewer', |
| 358 | 358 | (proj.imagedata, |
| 359 | 359 | proj.mask_dict)) |
| 360 | - ps.Publisher().sendMessage('Load slice plane') | |
| 360 | + #ps.Publisher().sendMessage('Load slice plane') | |
| 361 | 361 | ps.Publisher().sendMessage('Bright and contrast adjustment image',\ |
| 362 | 362 | (proj.window, proj.level)) |
| 363 | 363 | ps.Publisher().sendMessage('Update window level value',\ |
| ... | ... | @@ -409,7 +409,7 @@ class Controller(): |
| 409 | 409 | name_to_const[dicom.image.orientation_label] |
| 410 | 410 | proj.window = float(dicom.image.window) |
| 411 | 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 | 445 | bits = dicom.image.bits_allocad |
| 446 | 446 | sop_class_uid = dicom.acquisition.sop_class_uid |
| 447 | 447 | xyspacing = dicom.image.spacing |
| 448 | + orientation = dicom.image.orientation_label | |
| 448 | 449 | |
| 449 | 450 | if sop_class_uid == '1.2.840.10008.5.1.4.1.1.7': #Secondary Capture Image Storage |
| 450 | 451 | use_dcmspacing = 1 |
| 451 | 452 | else: |
| 452 | 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 | 460 | # 1(a): Fix gantry tilt, if any |
| 458 | 461 | tilt_value = dicom.acquisition.tilt |
| ... | ... | @@ -466,10 +469,23 @@ class Controller(): |
| 466 | 469 | tilt_value = -1*tilt_value |
| 467 | 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 | 477 | self.Slice = sl.Slice() |
| 471 | 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 | 489 | return imagedata, dicom |
| 474 | 490 | |
| 475 | 491 | def LoadImagedataInfo(self): | ... | ... |
invesalius/data/imagedata_utils.py
| ... | ... | @@ -456,13 +456,19 @@ class ImageCreator: |
| 456 | 456 | |
| 457 | 457 | return imagedata |
| 458 | 458 | |
| 459 | -def dcm2memmap(files, slice_size): | |
| 459 | +def dcm2memmap(files, slice_size, orientation): | |
| 460 | 460 | """ |
| 461 | 461 | From a list of dicom files it creates memmap file in the temp folder and |
| 462 | 462 | returns it and its related filename. |
| 463 | 463 | """ |
| 464 | 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 | 473 | matrix = numpy.memmap(temp_file, mode='w+', dtype='int16', shape=shape) |
| 468 | 474 | dcm_reader = vtkgdcm.vtkGDCMImageReader() |
| ... | ... | @@ -471,25 +477,43 @@ def dcm2memmap(files, slice_size): |
| 471 | 477 | dcm_reader.Update() |
| 472 | 478 | image = dcm_reader.GetOutput() |
| 473 | 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 | 490 | matrix.flush() |
| 478 | 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 | 494 | dy, dx = n_array.shape |
| 482 | 495 | n_array.shape = dx * dy |
| 483 | 496 | |
| 484 | 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 | 510 | # Generating the vtkImageData |
| 487 | 511 | image = vtk.vtkImageData() |
| 488 | - image.SetDimensions(dx, dy, 1) | |
| 489 | 512 | image.SetOrigin(0, 0, 0) |
| 490 | 513 | image.SetSpacing(spacing) |
| 491 | 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 | 517 | image.SetScalarType(numpy_support.get_vtk_array_type(n_array.dtype)) |
| 494 | 518 | image.AllocateScalars() |
| 495 | 519 | image.GetPointData().SetScalars(v_image) | ... | ... |
invesalius/data/slice_.py
| ... | ... | @@ -239,15 +239,11 @@ class Slice(object): |
| 239 | 239 | def GetSlices(self, orientation, slice_number): |
| 240 | 240 | if orientation == 'AXIAL': |
| 241 | 241 | n_array = numpy.array(self.matrix[slice_number]) |
| 242 | - spacing = self.spacing | |
| 243 | 242 | elif orientation == 'CORONAL': |
| 244 | 243 | n_array = numpy.array(self.matrix[..., slice_number, ...]) |
| 245 | - spacing = self.spacing[0], self.spacing[2], self.spacing[1] | |
| 246 | 244 | elif orientation == 'SAGITAL': |
| 247 | 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 | 247 | image = self.do_ww_wl(image) |
| 252 | 248 | return image |
| 253 | 249 | |
| ... | ... | @@ -619,11 +615,13 @@ class Slice(object): |
| 619 | 615 | ps.Publisher().sendMessage('Update slice viewer') |
| 620 | 616 | |
| 621 | 617 | def do_ww_wl(self, image): |
| 618 | + print self.window_width, self.window_level | |
| 619 | + print image.GetScalarRange() | |
| 622 | 620 | colorer = vtk.vtkImageMapToWindowLevelColors() |
| 623 | 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 | 625 | colorer.Update() |
| 628 | 626 | |
| 629 | 627 | return colorer.GetOutput() | ... | ... |
invesalius/data/viewer_slice.py
| ... | ... | @@ -173,7 +173,6 @@ class Viewer(wx.Panel): |
| 173 | 173 | self.cam = ren.GetActiveCamera() |
| 174 | 174 | self.ren = ren |
| 175 | 175 | |
| 176 | - | |
| 177 | 176 | def SetInteractorStyle(self, state): |
| 178 | 177 | self.state = state |
| 179 | 178 | action = {const.SLICE_STATE_CROSS: |
| ... | ... | @@ -1056,26 +1055,26 @@ class Viewer(wx.Panel): |
| 1056 | 1055 | return cursor |
| 1057 | 1056 | |
| 1058 | 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 | 1069 | #if slice_.imagedata is None: |
| 1068 | 1070 | #slice_.SetInput(imagedata, mask_dict) |
| 1069 | 1071 | |
| 1070 | - actor = vtk.vtkImageActor() | |
| 1071 | 1072 | ##actor.SetInput(slice_.GetOutput()) |
| 1072 | 1073 | #self.LoadRenderers(slice_.GetOutput()) |
| 1073 | 1074 | #self.__configure_renderers() |
| 1074 | 1075 | #ren = self.slice_data_list[0].renderer |
| 1075 | 1076 | #actor = self.slice_data_list[0].actor |
| 1076 | 1077 | #actor_bound = actor.GetBounds() |
| 1077 | - self.actor = actor | |
| 1078 | - self.ren.AddActor(self.actor) | |
| 1079 | 1078 | #self.cam = ren.GetActiveCamera() |
| 1080 | 1079 | |
| 1081 | 1080 | #for slice_data in self.slice_data_list: |
| ... | ... | @@ -1088,12 +1087,10 @@ class Viewer(wx.Panel): |
| 1088 | 1087 | |
| 1089 | 1088 | #if actor.GetSliceNumberMax() % number_of_slices: |
| 1090 | 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 | 1090 | #self.set_scroll_position(0) |
| 1095 | 1091 | |
| 1096 | 1092 | #actor_bound = actor.GetBounds() |
| 1093 | + self.interactor.Render() | |
| 1097 | 1094 | |
| 1098 | 1095 | #self.EnableText() |
| 1099 | 1096 | ## Insert cursor |
| ... | ... | @@ -1268,43 +1265,23 @@ class Viewer(wx.Panel): |
| 1268 | 1265 | renderer.AddViewProp(slice_data.box_actor) |
| 1269 | 1266 | return slice_data |
| 1270 | 1267 | |
| 1271 | - def __update_camera(self, slice_data): | |
| 1268 | + def __update_camera(self): | |
| 1272 | 1269 | orientation = self.orientation |
| 1273 | 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 | 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 | 1286 | def UpdateRender(self, evt): |
| 1310 | 1287 | self.interactor.Render() |
| ... | ... | @@ -1341,18 +1318,10 @@ class Viewer(wx.Panel): |
| 1341 | 1318 | |
| 1342 | 1319 | def OnScrollBar(self, evt=None): |
| 1343 | 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 | 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 | 1326 | def OnScrollBarRelease(self, evt): |
| 1358 | 1327 | #self.UpdateSlice3D(self.pos) |
| ... | ... | @@ -1409,43 +1378,14 @@ class Viewer(wx.Panel): |
| 1409 | 1378 | evt.Skip() |
| 1410 | 1379 | |
| 1411 | 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 | 1390 | def ChangeSliceNumber(self, pubsub_evt): |
| 1451 | 1391 | index = pubsub_evt.data | ... | ... |