def test_request_data_set(self): # Selector selector = odil.webservices.Selector() selector.set_study("1.2").set_instance("") # DataSet dataset = odil.DataSet() dataset.add(odil.Tag("PatientName"), ["TOTO"]) dataset.add(odil.Tag("00200020")) shared = odil.DataSet() shared.add(odil.Tag("StudyDate"), [20130509]) shared.add(odil.Tag("00200035")) dataset.add(odil.Tag("SharedFunctionalGroupsSequence"), [shared]) request = odil.webservices.QIDORSRequest(self.base_url_http) request.request_datasets( odil.webservices.Utils.Representation.DICOM_XML, selector, dataset) full_url_alphabetic_tags = odil.webservices.URL( "http", "example.com", "/dicom/studies/1.2/instances", "PatientName=TOTO&" "SharedFunctionalGroupsSequence.StudyDate=20130509&" "includefield=PatientOrientation&" "includefield=SharedFunctionalGroupsSequence.ImageOrientation&" "fuzzymatching=false", "") self.assertEqual(request.get_http_request().get_target(), full_url_alphabetic_tags) self.assertEqual(request.get_http_request().get_method(), "GET")
def test_get_stacks_inversion_times(self): # Test multiFrame dataSet ds = odil.DataSet() shared = odil.DataSet() per_frame = [] first_frame = odil.DataSet() mr_modifier_seq = odil.DataSet() mr_modifier_seq.add("InversionTimes", [300.]) first_frame.add("MRModifierSequence", [mr_modifier_seq]) per_frame.append(first_frame) second_frame = odil.DataSet() mr_modifier_seq.add("InversionTimes", [1000.]) second_frame.add("MRModifierSequence", [mr_modifier_seq]) per_frame.append(second_frame) third_frame = odil.DataSet() mr_modifier_seq.add("InversionTimes", [300.]) third_frame.add("MRModifierSequence", [mr_modifier_seq]) per_frame.append(third_frame) ds.add("SOPClassUID", [odil.registry.EnhancedMRImageStorage]) ds.add("PerFrameFunctionalGroupsSequence", per_frame) ds.add("SharedFunctionalGroupsSequence", [shared]) ds.add("NumberOfFrames", [len(per_frame)]) stacks = get_stacks([ds]) inversionTimes_tag = str(odil.Tag("InversionTimes")) mr_modifier_seq_tag = str(odil.Tag("MRModifierSequence")) per_frame_tag = str(odil.Tag("PerFrameFunctionalGroupsSequence")) wanted_stacks = { (((per_frame_tag, mr_modifier_seq_tag, inversionTimes_tag), (300., )), ): [(ds, 0), (ds, 2)], (((per_frame_tag, mr_modifier_seq_tag, inversionTimes_tag), (1000., )), ): [(ds, 1)], } self.assertEqual(wanted_stacks, stacks)
def test_hash(self): tag = odil.Tag( odil.registry.PatientName.group, odil.registry.PatientName.element) d = {tag: True} self.assertTrue(tag in d) self.assertTrue(odil.registry.PatientName in d) other = odil.Tag( odil.registry.PatientID.group, odil.registry.PatientID.element)
def test_query_dataset(self): http_request = odil.webservices.HTTPRequest("GET", self.full_url) http_request.set_header("Accept", "application/dicom+json") request = odil.webservices.QIDORSRequest(http_request) # Check For DataSet dataset = odil.DataSet() dataset.add(odil.Tag("PatientName"), ["TOTO"]) dataset.add(odil.Tag("00200020")) shared = odil.DataSet() shared.add(odil.Tag("EffectiveEchoTime"), [10.5]) shared.add(odil.Tag("00200035")) dataset.add(odil.Tag("SharedFunctionalGroupsSequence"), [shared]) self.assertEqual(request.get_query_data_set(), dataset)
def test_get_stacks_orientation(self): # Test with multi_frame dataSets (one dataSet will have the orientation stored in the # shared and the second in the per_frame) # This example is only here to show the importance of the tag location (per_frame or shared) ds = odil.DataSet() ds.add("SOPClassUID", [odil.registry.EnhancedMRImageStorage]) shared = odil.DataSet() plane_ori_seq = odil.DataSet() plane_ori_seq.add("ImageOrientationPatient", [1.0, 0.0, 0.0, 0.0, 1.0, 0.0]) shared.add("PlaneOrientationSequence", [plane_ori_seq]) per_frame = [odil.DataSet(), odil.DataSet()] # two frames both empty ds.add("SharedFunctionalGroupsSequence", [shared]) ds.add("PerFrameFunctionalGroupsSequence", per_frame) ds.add("NumberOfFrames", [len(per_frame)]) ds2 = odil.DataSet() ds2.add("SOPClassUID", [odil.registry.EnhancedMRImageStorage]) ds2.add("SharedFunctionalGroupsSequence", [odil.DataSet()]) per_frame = [] first_frame = odil.DataSet() plane_ori_seq = odil.DataSet() plane_ori_seq.add("ImageOrientationPatient", [1.0, 0.0, 0.0, 0.0, 1.0, 0.0]) first_frame.add("PlaneOrientationSequence", [plane_ori_seq]) per_frame.append(first_frame) second_frame = odil.DataSet() plane_ori_seq.add("ImageOrientationPatient", [1.0, 0.0, 0.0, 0.0, -1.0, 0.0]) second_frame.add("PlaneOrientationSequence", [plane_ori_seq]) per_frame.append(second_frame) ds2.add("PerFrameFunctionalGroupsSequence", per_frame) ds2.add("NumberOfFrames", [len(per_frame)]) stacks = get_stacks([ds, ds2]) imageOrientationTag = str(odil.Tag("ImageOrientationPatient")) plane_ori_seq_tag = str(odil.Tag("PlaneOrientationSequence")) per_frame_tag = str(odil.Tag("PerFrameFunctionalGroupsSequence")) shared_tag = str(odil.Tag("SharedFunctionalGroupsSequence")) wanted_stacks = { (((shared_tag, plane_ori_seq_tag, imageOrientationTag), (1.0, 0.0, 0.0, 0.0, 1.0, 0.0)), ): [(ds, 0), (ds, 1)], (((per_frame_tag, plane_ori_seq_tag, imageOrientationTag), (1.0, 0.0, 0.0, 0.0, 1.0, 0.0)), ): [(ds2, 0)], (((per_frame_tag, plane_ori_seq_tag, imageOrientationTag), (1.0, 0.0, 0.0, 0.0, -1.0, 0.0)), ): [(ds2, 1)], } self.assertEqual(stacks, wanted_stacks)
def test_contains(self): self.assertTrue( odil.registry.PatientName in odil.registry.public_dictionary) self.assertFalse( odil.Tag(0x0001, 0x0001) in odil.registry.public_dictionary) self.assertTrue("60xx0010" in odil.registry.public_dictionary) self.assertFalse("foo" in odil.registry.public_dictionary)
def test_default_finder(self): finder = odil.VRFinder() data_set = odil.DataSet() data_set.add(odil.registry.BitsAllocated, [8]) vr = finder( odil.Tag(0x7fe0, 0x0010), data_set, odil.registry.ExplicitVRLittleEndian) self.assertEqual(vr, odil.VR.OB)
def test_sort_in_stack_position(self): ds = odil.DataSet() stack_id_seq = odil.DataSet() stack_id_seq.add("DimensionOrganizationUID", ["1.3.6.1.4.1.5962.1.6.5012.1.0.1166546115.14677"]) stack_id_seq.add("DimensionIndexPointer", ["00209056"]) stack_id_seq.add("FunctionalGroupPointer", ["00209111"]) in_stack_seq = odil.DataSet() in_stack_seq.add("DimensionOrganizationUID", ["1.3.6.1.4.1.5962.1.6.5012.1.0.1166546115.14677"]) in_stack_seq.add("DimensionIndexPointer", ["00209057"]) in_stack_seq.add("FunctionalGroupPointer", ["00209111"]) ds.add("DimensionIndexSequence", [stack_id_seq, in_stack_seq]) shared = odil.DataSet() ds.add("SharedFunctionalGroupsSequence", [shared]) per_frame = [] first_frame = odil.DataSet() frame_content_seq = odil.DataSet() frame_content_seq.add("DimensionIndexValues", [10, 5]) # StackID , In Stack Pos first_frame.add("FrameContentSequence", [frame_content_seq]) per_frame.append(first_frame) second_frame = odil.DataSet() frame_content_seq.add("DimensionIndexValues", [10, 4]) second_frame.add("FrameContentSequence", [frame_content_seq]) per_frame.append(second_frame) third_frame = odil.DataSet() frame_content_seq.add("DimensionIndexValues", [10, 6]) third_frame.add("FrameContentSequence", [frame_content_seq]) per_frame.append(third_frame) ds.add("PerFrameFunctionalGroupsSequence", per_frame) ds.add("NumberOfFrames", [len(per_frame)]) data_sets_frame_index = [(ds, 0), (ds, 1), (ds, 2)] wanted_order = [(ds, 1), (ds, 0), (ds, 2)] per_frame_tag = str(odil.Tag("PerFrameFunctionalGroupsSequence")) frame_content_seq_tag = str(odil.Tag("FrameContentSequence")) dimension_index_values_tag = str(odil.Tag("DimensionIndexValues")) #input = key with value = (10,) element of DimensionIndexValue without inStackPosition key = ((per_frame_tag, frame_content_seq_tag, dimension_index_values_tag), (10, )) sort((key, ), data_sets_frame_index) print data_sets_frame_index self.assertEqual(wanted_order, data_sets_frame_index)
def test_get_stacks_echo_time(self): # Test single frame dataSets ds = [odil.DataSet() for x in range(3)] ds[0].add("SOPClassUID", [odil.registry.MRImageStorage]) ds[0].add("EchoTime", [25.]) ds[0].add("PatientName", ["Pacino^Al"]) ds[1].add("SOPClassUID", [odil.registry.MRImageStorage]) ds[1].add("EchoTime", [20.]) ds[2].add("SOPClassUID", [odil.registry.MRImageStorage]) ds[2].add("EchoTime", [25.]) stacks = get_stacks(ds) echoTime_tag = str(odil.Tag("EchoTime")) wanted_stacks = { (((None, None, echoTime_tag), (25., )), ): [(ds[0], None), (ds[2], None)], (((None, None, echoTime_tag), (20., )), ): [(ds[1], None)] } self.assertEqual(wanted_stacks, stacks)
def sort(keys, data_sets_frame_idx): """ Sort current stack frames/datasets depending on their common keys :param keys: Keys shared by all element of the stack :param data_sets_frame_idx: List containing the data sets of the frame with the corresponding frame when it's a multiframe data set """ number_of_frames = len(data_sets_frame_idx) if number_of_frames == 1: # WARNING : Can cause some problem when opening .nii file with Slicer logger.debug("Only one frame in the current stack") return else: for key in keys: for tags, value in keys: top_seq, sub_seq, tag = tags if str(odil.registry.DimensionIndexValues) == tag: # sort by In-Stack Position in_stack_position = [] for data_set, index in data_sets_frame_idx: in_stack_position_idx = odil_getter.get_in_stack_position_index( data_set) frame = data_set.as_data_set( odil.registry.PerFrameFunctionalGroupsSequence )[index] frame_content_seq = frame.as_data_set( odil.registry.FrameContentSequence)[0] in_stack_position.append( frame_content_seq.as_int( odil.registry.DimensionIndexValues) [in_stack_position_idx]) sorted_in_stack = sorted( range(len(in_stack_position)), key=lambda k: in_stack_position[k]) keydict = dict(zip(data_sets_frame_idx, sorted_in_stack)) data_sets_frame_idx.sort(key=keydict.get) return if str(odil.registry.ImageOrientationPatient) == tag: if sort_position(data_sets_frame_idx, value) == True: return available_tags = [x[0][2] for x in keys if len(x) > 1] logger.warning( "Cannot sort frames for the moment, available tags : {}".format( [odil.Tag(x).get_name() for x in available_tags]))
def convert_element(element, specific_character_set): """ Convert a DICOM element to its NIfTI+JSON representation: the "Value" (or "InlineBinary") attribute of its standard DICOM JSON representation. """ result = None # element can be None because a get is called above if element is None: result = None elif element.empty(): result = None elif element.is_int(): result = list(element.as_int()) elif element.is_real(): result = list(element.as_real()) elif element.is_string(): if element.vr.name in ["LO", "LT", "PN", "SH", "ST", "UT"]: data_set = odil.DataSet() if specific_character_set: data_set.add(odil.registry.SpecificCharacterSet, specific_character_set) data_set.add(odil.Tag(0xffff, 0xffff), element.as_string(), element.vr) result = json.loads(odil.as_json(data_set))["ffffffff"]["Value"] else: result = list(element.as_string()) elif element.is_data_set(): result = [ convert_data_set(x, specific_character_set) for x in element.as_data_set() ] elif element.is_binary(): result = [ base64.b64encode(x.get_memory_view().tobytes()) for x in element.as_binary() ] else: raise Exception("Unknown element type") return result
def get_in_stack_position_index(data_set): """ Will return the position of in-Stack-Position element in the dimensionIndexValue Return None if no In-Stack-Position found """ if data_set.has(odil.registry.DimensionIndexSequence) and \ not data_set.empty(odil.registry.DimensionIndexSequence): dimension_index_sequences = data_set.as_data_set( odil.registry.DimensionIndexSequence) position = set() for i, dimension_index_sequence in enumerate(dimension_index_sequences): if dimension_index_sequence.has(odil.registry.DimensionIndexPointer): idx = dimension_index_sequence.as_string( odil.registry.DimensionIndexPointer)[0] if odil.Tag(idx) == odil.registry.InStackPositionNumber: position.add(i) if len(position) == 1: return list(position)[0] else: return None else: return None
def test_string_constructor(self): tag = odil.Tag("PatientID") self.assertEqual(tag.group, 0x0010) self.assertEqual(tag.element, 0x0020)
def test_str(self): tag = odil.Tag(0x1234, 0x5678) self.assertEqual(str(tag), "12345678")
def test_greater_or_equal(self): tag1 = odil.Tag(0x1234, 0x5678) tag2 = odil.Tag(0x4321, 0x8765) self.assertFalse(tag1 >= tag2) self.assertTrue(tag1 >= tag1) self.assertTrue(tag2 >= tag1)
def test_less_or_equal(self): tag1 = odil.Tag(0x1234, 0x5678) tag2 = odil.Tag(0x4321, 0x8765) self.assertTrue(tag1 <= tag2) self.assertTrue(tag1 <= tag1) self.assertFalse(tag2 <= tag1)
def test_two_ints_constructor(self): tag = odil.Tag(0x1234, 0x5678) self.assertEqual(tag.group, 0x1234) self.assertEqual(tag.element, 0x5678)
def test_inequality(self): tag1 = odil.Tag(0x1234, 0x5678) tag2 = odil.Tag(0x4321, 0x8765) self.assertFalse(tag1 != tag1) self.assertTrue(tag1 != tag2)
def test_get_name(self): tag = odil.Tag(0x0010, 0x0020) self.assertEqual(tag.get_name(), "PatientID")
def test_is_private(self): public = odil.Tag(0x1234, 0x5678) self.assertTrue(not public.is_private()) private = odil.Tag(0x1235, 0x5678) self.assertTrue(private.is_private())
def test_element(self): tag = odil.Tag(0x1234, 0x5678) tag.element = 0x8765 self.assertEqual(tag.group, 0x1234) self.assertEqual(tag.element, 0x8765)
def test_get_tag_name(self): patient_name_tag = odil.Tag("PatientName") tag_name = dicomifier.dicom_to_nifti.meta_data.get_tag_name( patient_name_tag) self.assertEqual("PatientName", tag_name)
def get_image(data_sets_frame_idx, dtype, cache): """ Get the nifti image of the current stack :param data_sets_frame_idx: List containing the data sets of the frame with the corresponding frame when it's a multiframe data set :param dtype: wanted type :return image: a nifti image """ # Cache the linear pixel data since this is a time-consuming operation for # large data sets and needs to be repeated for multi-frame data sets. for data_set, frame_idx in data_sets_frame_idx: if data_set.as_string("SOPInstanceUID")[0] not in cache: linear_array = get_linear_pixel_data(data_set) cache[data_set.as_string("SOPInstanceUID")[0]] = linear_array pixel_data_list = [ get_shaped_pixel_data(data_set, cache[data_set.as_string("SOPInstanceUID")[0]], frame_idx) for data_set, frame_idx in data_sets_frame_idx ] if dtype is None: has_float = any(x.dtype.kind == "f" for x in pixel_data_list) if has_float: dtype = numpy.float32 else: # Assume all data sets have the same type dtype = pixel_data_list[0].dtype logger.info("dtype deduced to be: {}".format(dtype)) pixel_data = numpy.ndarray( (len(data_sets_frame_idx), ) + pixel_data_list[0].shape, dtype=dtype) for i, data in enumerate(pixel_data_list): pixel_data[i] = data scanner_transform = numpy.identity(4) origin, spacing, direction = get_geometry(data_sets_frame_idx) image_type = (data_sets_frame_idx[0][0].as_string("ImageType") if "ImageType" in data_sets_frame_idx[0][0] else []) if len(data_sets_frame_idx ) == 1 and b"MOSAIC" in image_type and "00291010" in data_set: data_set = data_sets_frame_idx[0][0] siemens_data = data_set.as_binary(odil.Tag("00291010"))[0] siemens_header = siemens.parse_csa( siemens_data.get_memory_view().tobytes()) number_of_images_in_mosaic = siemens_header["NumberOfImagesInMosaic"][ 0] tiles_per_line = int(math.ceil(math.sqrt(number_of_images_in_mosaic))) mosaic_shape = numpy.asarray(pixel_data.shape[-2:]) rows = round(pixel_data.shape[-2] / tiles_per_line) columns = round(pixel_data.shape[-1] / tiles_per_line) real_shape = numpy.asarray([rows, columns]) # Re-arrange array so that tiles are contiguous pixel_data = pixel_data.reshape(tiles_per_line, rows, tiles_per_line, columns) pixel_data = pixel_data.transpose((0, 2, 1, 3)) pixel_data = pixel_data.reshape(tiles_per_line**2, rows, columns) # Get the origin of the tiles (i.e. origin of the first tile), cf. # http://nipy.org/nibabel/dicom/dicom_mosaic.html # WARNING: need to invert their rows and columns R = direction[:, :2] Q = R * spacing[:2] origin = origin + \ numpy.dot(Q, (mosaic_shape[::-1] - real_shape[::-1]) / 2.) direction[:, 2] = siemens_header["SliceNormalVector"] samples_per_pix = data_sets_frame_idx[0][0].as_int("SamplesPerPixel")[0] if samples_per_pix == 1: lps_to_ras = [[-1, 0, 0], [0, -1, 0], [0, 0, 1]] scanner_transform[:3, :3] = numpy.dot(lps_to_ras, direction) scanner_transform[:3, :3] = numpy.dot(scanner_transform[:3, :3], numpy.diag(spacing)) scanner_transform[:3, 3] = numpy.dot(lps_to_ras, origin) elif samples_per_pix == 3 and data_sets_frame_idx[0][0].as_string( "PhotometricInterpretation")[0] == b"RGB": if dtype != numpy.uint8: raise Exception("Invalid dtype {} for RGB".format(dtype)) pixel_data = pixel_data.view( nibabel.nifti1.data_type_codes.dtype["RGB"]).reshape( pixel_data.shape[:3]) # WARNING: ArrayWriter.to_fileobj is Fortran-order by default whereas numpy # is C-order by default return nibabel.Nifti1Image(pixel_data.T, scanner_transform)
def test_group(self): tag = odil.Tag(0x1234, 0x5678) tag.group = 0x4321 self.assertEqual(tag.group, 0x4321) self.assertEqual(tag.element, 0x5678)
def test_cpp_specific(self): tag = odil.Tag(0xdead, 0xbeef) with self.assertRaises(odil.Exception): tag.get_name()