# Multiple series in a single CT series

**URL:** https://discourse.itk.org/t/multiple-series-in-a-single-ct-series/1525
**Category:** Beginner Questions
**Tags:** dicom
**Created:** [January 16, 2019, 9:31pm UTC](https://discourse.itk.org/t/multiple-series-in-a-single-ct-series/1525 "2019-01-16T21:31:51Z")
**Posts on this page:** 19
**Page:** 1

<div class="post-metadata">

### Author: ![hadasara](https://discourse.itk.org/letter_avatar_proxy/v4/letter/h/c67d28/32.png) [@hadasara](https://discourse.itk.org/u/hadasara)
#### Post date: [January 16, 2019, 9:31pm UTC](https://discourse.itk.org/t/multiple-series-in-a-single-ct-series/1525/1 "2019-01-16T21:31:51Z")

</div>

Hi all,  
Maybe someone here would know-  
I came across a CT series, that include 2 series, saved as a single one.  
When you scroll in axial slices, you see shoulder s to pelvis and then agin shoulders to pelvis in a later contrast.  
Does anyone know if there is a tag in the DICOM header that can seperate them?  
ITK-SNAP doesn’t recognize they are seperate, and also from viewing the header I could’nt find anything.

Suggestions?  
Thanks!

---

<div class="post-metadata">

### Author: ![fbudin](https://discourse.itk.org/user_avatar/discourse.itk.org/fbudin/32/14_2.png) [@fbudin](https://discourse.itk.org/u/fbudin)
#### Post date: [January 16, 2019, 9:57pm UTC](https://discourse.itk.org/t/multiple-series-in-a-single-ct-series/1525/2 "2019-01-16T21:57:22Z")

</div>

If you are using `itk.GDCMSeriesFileNames`, you can try to use `SetUseSeriesDetails(True)` (link to doxygen documentation)

---

<div class="post-metadata">

### Author: ![dzenanz](https://discourse.itk.org/user_avatar/discourse.itk.org/dzenanz/32/1093_2.png) [@dzenanz](https://discourse.itk.org/u/dzenanz)
#### Post date: [January 16, 2019, 10:14pm UTC](https://discourse.itk.org/t/multiple-series-in-a-single-ct-series/1525/3 "2019-01-16T22:14:32Z")

</div>

You can also try loading it in [Slicer](https://slicer.org/), to see if they are separated/loaded properly. Slicer uses ITK for image loading, with extra logic for better handling of DICOMs.

---

<div class="post-metadata">

### Author: ![hadasara](https://discourse.itk.org/letter_avatar_proxy/v4/letter/h/c67d28/32.png) [@hadasara](https://discourse.itk.org/u/hadasara)
#### Post date: [January 16, 2019, 10:18pm UTC](https://discourse.itk.org/t/multiple-series-in-a-single-ct-series/1525/4 "2019-01-16T22:18:53Z")

</div>

Thank you!  
SimpleITK (which I use in python ) seems to not have SetUseSeriesDetails function…

---

<div class="post-metadata">

### Author: ![fbudin](https://discourse.itk.org/user_avatar/discourse.itk.org/fbudin/32/14_2.png) [@fbudin](https://discourse.itk.org/u/fbudin)
#### Post date: [January 16, 2019, 10:22pm UTC](https://discourse.itk.org/t/multiple-series-in-a-single-ct-series/1525/5 "2019-01-16T22:22:26Z")

</div>

@blowekamp: Do you have any idea why the flag mentioned in the discussion seems to not be exposed in SimpleITK? Any idea how to access it?

---

<div class="post-metadata">

### Author: ![hadasara](https://discourse.itk.org/letter_avatar_proxy/v4/letter/h/c67d28/32.png) [@hadasara](https://discourse.itk.org/u/hadasara)
#### Post date: [January 16, 2019, 10:22pm UTC](https://discourse.itk.org/t/multiple-series-in-a-single-ct-series/1525/6 "2019-01-16T22:22:56Z")

</div>

Thank you!  
this is the error I got in Slicer:

Imported a DICOM directory, checking for extensions  
Imported a DICOM directory, checking for extensions  
Geometric issues were found with 1 of the series. Please use caution.  
Warning in DICOM plugin Scalar Volume when examining loadable 2: Unnamed Series: Images are not equally spaced (a difference of -0.5 vs 1 in spacings was detected). If loaded image appears distorted, enable ‘Acquisition geometry regularization’ in Application settins / DICOM / DICOMScalarVolumePlugin. Please use caution.  
Loading with imageIOName: GDCM  
Window/level found in DICOM tags (center=40.0, width=350.0) has been applied to volume 2: Unnamed Series  
Irregular volume geometry detected (maximum error of 218 mm is above tolerance threshold of 0.001 mm). Regularization transform is not added, as the option is disabled.

---

<div class="post-metadata">

### Author: ![fbudin](https://discourse.itk.org/user_avatar/discourse.itk.org/fbudin/32/14_2.png) [@fbudin](https://discourse.itk.org/u/fbudin)
#### Post date: [January 17, 2019, 1:29am UTC](https://discourse.itk.org/t/multiple-series-in-a-single-ct-series/1525/7 "2019-01-17T01:29:16Z")

</div>

You may want to post this error message on the Slicer discourse [here](https://discourse.slicer.org). They should be able to tell you if they use the flag I mentioned above or something similar.

---

<div class="post-metadata">

### Author: ![hadasara](https://discourse.itk.org/letter_avatar_proxy/v4/letter/h/c67d28/32.png) [@hadasara](https://discourse.itk.org/u/hadasara)
#### Post date: [January 17, 2019, 10:48am UTC](https://discourse.itk.org/t/multiple-series-in-a-single-ct-series/1525/8 "2019-01-17T10:48:13Z")

</div>

Thanks! I will.

---

<div class="post-metadata">

### Author: ![blowekamp](https://discourse.itk.org/user_avatar/discourse.itk.org/blowekamp/32/79_2.png) [@blowekamp](https://discourse.itk.org/u/blowekamp)
#### Post date: [January 17, 2019, 2:02pm UTC](https://discourse.itk.org/t/multiple-series-in-a-single-ct-series/1525/9 "2019-01-17T14:02:38Z")

</div>

In SimpleITK the [ImageSeriesReader\_GetGDCMSeriesFileNames](https://itk.org/SimpleITKDoxygen/html/classitk_1_1simple_1_1ImageSeriesReader.html#a148badf23595d59e34b1d9360c79b539) static member function has a `useSeriesDetails` argument which can be set to true.

You can find an example of the usage of the function here:  
[https://simpleitk.readthedocs.io/en/master/Examples/DicomSeriesReader/Documentation.html](https://simpleitk.readthedocs.io/en/master/Examples/DicomSeriesReader/Documentation.html)

---

<div class="post-metadata">

### Author: ![hadasara](https://discourse.itk.org/letter_avatar_proxy/v4/letter/h/c67d28/32.png) [@hadasara](https://discourse.itk.org/u/hadasara)
#### Post date: [January 17, 2019, 2:42pm UTC](https://discourse.itk.org/t/multiple-series-in-a-single-ct-series/1525/10 "2019-01-17T14:42:10Z")

</div>

Oh! it’s an argument Thanks!  
Unfortunatly, it didn’t work.

I tried to run this code, but it generate an image with all slices, from 2 series:

(my dir= directory to the CT DICOM images)

reader = sitk.ImageSeriesReader()  
dicom\_names=sitk.ImageSeriesReader\_GetGDCMSeriesFileNames(rmydir) ,useSeriesDetails=True)  
reader.SetFileNames(dicom\_names)  
image = reader.Execute()  
size = image.GetSize()

If anyone has another suggestions, it would be great.

---

<div class="post-metadata">

### Author: ![hadasara](https://discourse.itk.org/letter_avatar_proxy/v4/letter/h/c67d28/32.png) [@hadasara](https://discourse.itk.org/u/hadasara)
#### Post date: [January 17, 2019, 2:57pm UTC](https://discourse.itk.org/t/multiple-series-in-a-single-ct-series/1525/11 "2019-01-17T14:57:23Z")

</div>

@bsmarine maybe you could advise?

---

<div class="post-metadata">

### Author: ![blowekamp](https://discourse.itk.org/user_avatar/discourse.itk.org/blowekamp/32/79_2.png) [@blowekamp](https://discourse.itk.org/u/blowekamp)
#### Post date: [January 17, 2019, 3:01pm UTC](https://discourse.itk.org/t/multiple-series-in-a-single-ct-series/1525/12 "2019-01-17T15:01:57Z")

</div>

What is returned from [`ImageSeriesReader_GetGDCMSeriesIDs(rmydir)`](https://itk.org/SimpleITKDoxygen/html/classitk_1_1simple_1_1ImageSeriesReader.html#a7243e837620b6e125c86fb92ca9bdeb8)?

The `GetCDCMSeriesFileNames` method can also take a `seriesID` argument which may help separate the two.

This would be the standard way of getting access to a specific series when multiple series are in a directory. However, given that Slicer did not automatically detect this it likely will have some type of problem. You likely need to your data provenance such as how it has been created and modified as it may not be “valid DICOM”. Inspecting the DICOM tags to “hack” a split of the slices may work.

---

<div class="post-metadata">

### Author: ![fedorov](https://discourse.itk.org/user_avatar/discourse.itk.org/fedorov/32/207_2.png) [@fedorov](https://discourse.itk.org/u/fedorov)
#### Post date: [January 18, 2019, 5:02pm UTC](https://discourse.itk.org/t/multiple-series-in-a-single-ct-series/1525/13 "2019-01-18T17:02:44Z")

</div>

> [@hadasara](#):
>
> Suggestions?

I would look at the variety of standalone converters that already exist, some of them are summarized here: [NA-MIC Project Weeks | Website for NA-MIC Project Weeks](https://na-mic.github.io/ProjectWeek/PW27_2018_Boston/Projects/DICOMVolumeReconstruction/).

Personally, I would recommend [dcm2niix](https://github.com/rordenlab/dcm2niix) as the first tool to check out.

---

<div class="post-metadata">

### Author: ![fedorov](https://discourse.itk.org/user_avatar/discourse.itk.org/fedorov/32/207_2.png) [@fedorov](https://discourse.itk.org/u/fedorov)
#### Post date: [January 18, 2019, 5:03pm UTC](https://discourse.itk.org/t/multiple-series-in-a-single-ct-series/1525/14 "2019-01-18T17:03:54Z")

</div>

Another tool that I found very useful is [https://github.com/pieper/dicomsort](https://github.com/pieper/dicomsort), you can use it to separate the content of your directly based on the values of defined tags.

---

<div class="post-metadata">

### Author: ![hadasara](https://discourse.itk.org/letter_avatar_proxy/v4/letter/h/c67d28/32.png) [@hadasara](https://discourse.itk.org/u/hadasara)
#### Post date: [January 20, 2019, 8:57pm UTC](https://discourse.itk.org/t/multiple-series-in-a-single-ct-series/1525/15 "2019-01-20T20:57:07Z")

</div>

Thanks! I will look into it

---

<div class="post-metadata">

### Author: ![hadasara](https://discourse.itk.org/letter_avatar_proxy/v4/letter/h/c67d28/32.png) [@hadasara](https://discourse.itk.org/u/hadasara)
#### Post date: [January 20, 2019, 9:47pm UTC](https://discourse.itk.org/t/multiple-series-in-a-single-ct-series/1525/16 "2019-01-20T21:47:54Z")

</div>

For now I only found that the only thing that might separate the 2 series is acquisition time and location in space.

---

<div class="post-metadata">

### Author: ![mat\_vi](https://discourse.itk.org/user_avatar/discourse.itk.org/mat_vi/32/3614_2.png) [@mat\_vi](https://discourse.itk.org/u/mat_vi)
#### Post date: [July 28, 2023, 5:15pm UTC](https://discourse.itk.org/t/multiple-series-in-a-single-ct-series/1525/17 "2023-07-28T17:15:21Z")

</div>

I have had a similar problem with a series with 4 different acquistions. Separating by acquistion time seems to be the only way to split the series. I have done this using pydicom and dicom2nifti. Using this, I have separated the 4 different series which I can view independently on 3D slicer.

---

<div class="post-metadata">

### Author: ![matt.mccormick](https://discourse.itk.org/user_avatar/discourse.itk.org/matt.mccormick/32/7_2.png) [@matt.mccormick](https://discourse.itk.org/u/matt.mccormick)
#### Post date: [July 28, 2023, 7:08pm UTC](https://discourse.itk.org/t/multiple-series-in-a-single-ct-series/1525/18 "2023-07-28T19:08:47Z")

</div>

Also quite helpful is the new `series_uid` argument to `itk.imread` available in `itk-5.4rc01`.

---

<div class="post-metadata">

### Author: ![tmathai](https://discourse.itk.org/user_avatar/discourse.itk.org/tmathai/32/4557_2.png) [@tmathai](https://discourse.itk.org/u/tmathai)
#### Post date: [July 28, 2025, 6:57pm UTC](https://discourse.itk.org/t/multiple-series-in-a-single-ct-series/1525/19 "2025-07-28T18:57:53Z")

</div>

I followed most of the suggestions above (`useSeriesDetails `, `dcm2niix`, Steve Pieper’s code etc.), but none of them worked for me. The issue is that if there are systemic problems with the way the DICOMs were acquired/saved, then it is very challenging to obtain the sub-components of each series. This has to do with legacy reasons (older scanners, different FOV saved as a “new” acquisition etc.).

Anyway, I wrote my own little script to read all the files for a given study, separate them into individual series and sub-series (based on acquisition number), and then dump them all to disk. It uses `SimpleITK` and `pydicom`, so adapt for your own needs. Trust but verify yourself.

```auto
import SimpleITK as sitk 
import pydicom 

def extract_series_and_dump(
								dir_study_DICOM,
								dir_save_NII
							):
		
	print('--' * 25)
	print('idx:', row_idx)

	print('dir_study_DICOM')
	print(dir_study_DICOM)

	print('dir_save_NII')
	print(dir_save_NII)
	
	## check if folder exists
	if os.path.isdir(dir_study_DICOM) == True:

		print('Processing study:')
		print(dir_study_DICOM)	

		"""
		First use GDCM to get a rough division of filenames for each series UID
		"""
		
		## get all series IDs in this study
		l_series_IDs = sitk.ImageSeriesReader.GetGDCMSeriesIDs(dir_study_DICOM, useSeriesDetails=True)

		if not l_series_IDs:
			return False

		num_series_found = len(l_series_IDs)
		
		if num_series_found > 1:
			print('num_series_found:', num_series_found)

		"""
		iterate + identify any sub-acquisitions in each series
		"""

		for idx in range(0, num_series_found):
			
			series_UID = l_series_IDs[idx]

			print('--' * 25)
			print('idx:', idx)
			print('seriesID:', series_UID)

			## get all filenames for this series
			series_file_names = sitk.ImageSeriesReader.GetGDCMSeriesFileNames(dir_study_DICOM, series_UID, useSeriesDetails=True)

			"""
			identify any sub-acquisitions in this series
			----- use ACQUISITION NUMBER DICOM TAG -----
			"""

			d_series_by_acquisition_number = {}

			for i, file_name in enumerate(series_file_names):				
				
				file_reader = sitk.ImageFileReader()
				## set filename
				file_reader.SetFileName(file_name)
				## set flags
				file_reader.LoadPrivateTagsOn()
				## read file info
				file_reader.ReadImageInformation()

				## Get the list of metadata keys (DICOM tags)
				meta_data_keys = file_reader.GetMetaDataKeys()			

				## get acquisition number
				if "0020|0012" in meta_data_keys:					
					acquisition_number = file_reader.GetMetaData('0020|0012')
				else:
					continue
				
				## get acquisition time
				if "0008|0032" in meta_data_keys:					
					acquisition_time = file_reader.GetMetaData('0008|0032')		
				else:
					acquisition_time = ''
				
				## get series time 
				if "0008|0031" in meta_data_keys:					
					series_time = file_reader.GetMetaData('0008|0031')
				else:
					series_time = ''

				## get location of slice
				# instance_number = file_reader.GetMetaData("0020|0032").split("\\")[-1]
				pydf = pydicom.dcmread(file_name)
				## get pydicom slice location
				if hasattr(pydf, 'SliceLocation'):
					# print('SliceLocation:', pydf.SliceLocation)
					instance_number = pydf.SliceLocation
				else:
					instance_number = -1

				## protocol name
				if "0018|103E" in meta_data_keys:					
					protocol_name = file_reader.GetMetaData("0018|103E")
				else:
					protocol_name = ''

				## series description
				if "0008|103E" in meta_data_keys:					
					series_description = file_reader.GetMetaData("0008|103E")
				else:
					series_description = ''

				# print('acquisition_number:', acquisition_number, 'acquisition_time:', acquisition_time)
				# print('series_time:', series_time)
				# print('instance_number:', instance_number)
				# print('protocol_name:', protocol_name)
				# print('series_description:', series_description)

				key_ = acquisition_number

				if key_ not in d_series_by_acquisition_number:
					d_series_by_acquisition_number[key_] = {}
					## create
					d_series_by_acquisition_number[key_]['files'] = []
					d_series_by_acquisition_number[key_]['instances'] = []
					## store
					d_series_by_acquisition_number[key_]['files'].append(file_name)
					d_series_by_acquisition_number[key_]['instances'].append(instance_number)
				else:
					d_series_by_acquisition_number[key_]['files'].append(file_name)
					d_series_by_acquisition_number[key_]['instances'].append(instance_number)

			## how many?
			print('num_sub_series:', len(d_series_by_acquisition_number))

			"""
			Sort files in each sub-component by the position in volume 
			"""

			d_sorted = {}

			for key_ in d_series_by_acquisition_number:

				## get
				tfiles_ = d_series_by_acquisition_number[key_]['files']
				tinstances_ = d_series_by_acquisition_number[key_]['instances']

				## sort by position
				sorted_files, sorted_instances = zip(*sorted(zip(tfiles_, tinstances_)))

				## flip direction of files 
				sorted_files = sorted_files[::-1]
			
				d_sorted[key_] = {}
				## store
				d_sorted[key_]['files'] = sorted_files
				d_sorted[key_]['instances'] = sorted_instances

			## how many?
			print('num_sub_series:', len(d_sorted))

			num_sub_series = len(d_sorted)

			"""
			Dump each sub-component + JSON to disk 
			"""

			for tidx, key_ in enumerate(d_sorted):

				l_sorted_files = d_sorted[key_]['files']

				## initialize
				series_reader = sitk.ImageSeriesReader()
				## set
				series_reader.SetFileNames(l_sorted_files)
				## set flags
				series_reader.MetaDataDictionaryArrayUpdateOn()
				series_reader.LoadPrivateTagsOn()						
				## read 
				image_dicom = series_reader.Execute()

				## Create the file reader and get the series_ID
				file_reader = sitk.ImageFileReader()			
				## read file 
				temp_file_2_read = l_sorted_files[0]
				# print(l_file_names[0])
				## set filename
				file_reader.SetFileName(temp_file_2_read)
				## set flags
				file_reader.LoadPrivateTagsOn()
				## read file info
				file_reader.ReadImageInformation()

				## get metadata
				metadata = {}
				for key in file_reader.GetMetaDataKeys():

					## get group + element
					str_tag_group, str_tag_element = key.split('|')

					## convert to HEX
					hex_tag_group = hex(int(str_tag_group, 16))
					hex_tag_element = hex(int(str_tag_element, 16))

					## get keyword from tag using pydicom
					tag = pydicom.tag.Tag(hex_tag_group, hex_tag_element)
					keyword = pydicom.datadict.keyword_for_tag(tag)

					## store
					metadata[keyword] = file_reader.GetMetaData(key)

				## get StudyID
				temp_study_ID = file_reader.GetMetaData('0020|0010')
				## get SeriesID
				temp_series_ID = file_reader.GetMetaData('0020|0011')				

					
				## save image				
				if num_sub_series == 1:
					## only one acquisition, save as is
					fn_save = str(temp_study_ID).replace(" ", "") + '_' + str(temp_series_ID).replace(" ", "")
				else:
					## more than one acquisition, ID of this one 
					fn_save = str(temp_study_ID).replace(" ", "") + '_' + str(temp_series_ID).replace(" ", "") + '_acq' + str(tidx + 1)
				
				## save
				ffpn_save_image = os.path.join(dir_save_NII, fn_save + ".nii.gz")

				print('study_series:')
				print(fn_save)

				## write
				sitk.WriteImage(image_dicom, ffpn_save_image)

				## save metadata
				ffpn_save_metadata_json = os.path.join(dir_save_NII, fn_save + ".json")		

				## write metadata to disk
				with open(ffpn_save_metadata_json, 'w') as f:
					json.dump(metadata, f, indent=4) # indent for pretty printing

	return True

```
