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 | ... | ... |