How to write a DicomVolume from a DicomSeries without losing Header Infos.

Hi everyone,

I have read the DicomSeriesReadImageWrite2 example .

It did work but i noticed the Dicom-header Infos are “losted”.
All of slices have the same Dicom-header Infos and pixel spacing will be default 1. I tired many options about SetMetaDataDictionary/SetMetaDataDictionaryAarry but the problem persisted. Here is my code. I’ve simplified the “reader part”. of above example

using PixelType = signed short;
	constexpr unsigned int Dimension = 3;
	using ImageType = itk::Image<PixelType, Dimension>;

	using NamesGeneratorType = itk::GDCMSeriesFileNames;
	NamesGeneratorType::Pointer NameGenerator = NamesGeneratorType::New();

	NameGenerator->SetUseSeriesDetails(true);
	NameGenerator->AddSeriesRestriction("0008|0021");

	NameGenerator->SetGlobalWarningDisplay(false);
	NameGenerator->SetDirectory(myInputDirectory);
 using ReaderType = itk::ImageSeriesReader<ImageType>;
	ReaderType::Pointer reader = ReaderType::New();
	using ImageIOType = itk::GDCMImageIO;
	ImageIOType::Pointer dicomIO = ImageIOType::New();
	const ReaderType::FileNamesContainer & filenames = NameGenerator->GetInputFileNames();
	reader->SetImageIO(dicomIO);
	reader->SetFileNames(filenames);
//reader->ForceOrthogonalDirectionOff();
			try {
				reader->Update();
			}
			catch (itk::ExceptionObject& ex) {
				std::cout << ex << std::endl;

			}
	using ImageoutType = itk::Image<PixelType, 3>;
			using WriterType = itk::ImageSeriesWriter<ImageType, ImageoutType>;
			WriterType::Pointer writer = WriterType::New();
			writer->SetInput(reader->GetOutput());
			writer->SetImageIO(dicomIO);
			writer->SetFileName(myOutputDirectory+"\\xxx.dcm")
			writer->SetMetaDataDictionary(reader->GetMetaDataDictionary());

			try {
				writer->Update();
			}
			catch (itk::ExceptionObject& ex) {
				std::cout << ex << std::endl;
			}

You taking n 2D files, and producing 1 3D file. There can be only 1 metadata item for regular stuff (e.g. flip angle), even if the metadata item in question had different values among input 2D slices. I am not sure whether you can save an array of metadata, but even if you can it is non-trivial DICOM trickery. @lassoan, @mihail.isakov or @mathieu.malaterre might offer more insight.

Thanks for your reply. Yes it is. There is only one metadata from n slice apply for volume I created. in this case all the slice in volume have the same image number and pixels spacing infos will lost.that is annoying. Furthermore there is also a tricky problem. When I use imageJ read the volume, and check the metadata ,Tag “0028|0030” did have the original XY pixel spacing but there is a symbol"<" before the key. Then I use" variable= dicomIo-> GetvaluefromTag(“0028|0031”, value)” variable will not be given any value which means this Tag is empty . I am confused

FYI

1 Like

So if i want to keep the metadata for each frame then i have to use this toolkit ?
Is it possiable that i only use ITK to achieve this function?

You can simply load image and save ITK image with .dcm extension with ITK writer, the result will be Multi-frame Grayscale Word (or byte or true color) Secondary Capture Image, it works good (for uniform images) and the image will be multi-frame too. Theoretically, in ITK you have access to DCMTK and GDCM libraries, so at very low level is mostly everything possible. But the task is rather difficult, IMHO. There are many question, what storage class do you want to generate, etc.

Edit:
I hope i have understood the task correctly, from the title “DicomVolume from DicomSeries” i have assumed you wish to create multi-frame image from classic series. They are different storage classes, eg, MR Image Storage and Enhanced MR Image Storage or Legacy Converted Enhanced MR Image Storage.

1 Like

Thanks for details reply. You understand my question right. I knew how to create a multi frame image. But the problem is that how can I create a multi frame dicom while it can also keep the original metadata for each frame. As you said they are different storage classes , so if I simply use itkwriter will not avoid losing the metadata . Am I right?

If you simply use ITK writer - spacing, origin, orientation should be correct (for multi-frame secondary capture s. content of shared and per frame groups with e.g. dcmdump). Caution with non-uniform series, 3D volume is assumed. By default there will be no patient name/id/study id, but it is possible to add . If you don’t want multi-frame secondary capture, but e.g.convert mr classic series to mr enhanced or have non-uniform series - better try David Clunie’s java class, it could be fastest and correct solution.

Edit:
with uniform i mean orthogonal and with equidistant slices, spacing can be different for all 3 axis

1 Like

The Series is uniform and from the same CT scan sequence. Even though the Multi-Frame Image can not keep the Pixelspacing.

I show you the screenshot from ImageJ ,the first two images are from Multi-Frame and last two images are from a single slice of orginal Series.
.Snipaste_2020-09-09_00-43-13 Snipaste_2020-09-09_00-46-05 Snipaste_2020-09-09_00-46-57 Snipaste_2020-09-09_00-47-29

Tag “0028|0030” represent the Pixelspacing ,and in Multi-Frame file I created ,there is a symbol"<" before the key Pixelspacing . and i wrorte the code:

std::string value;
dicomIo-> GetvaluefromTag(“0028|0030”, value)

value will not be assigned any value. And at the end of Metadata list, the ImageJ self-analyzed part,the pixel spacing is considered as 1 ,but it should be 0.085.
This problem is due to the mechanism of dicom or i made mistake in my code? (code is above)
Thanks again

What voxel spacing ITK-SNAP and 3D Slicer report for TOMO_unpadded.IMA?

In multi-frame (sc or mr or ct) pixel spacing should be in shared or per-frame group, not at the root level like in single frame.

There should be no pixel spacing tag at root level.

To my surprise, in 3D slicer Image spacing is correct . But it doesn’t solve the problem that spacing not being able to read it in the code.

root level means the single frame in Multi frame file, right?

I seem to have a clue. That is, for multiframe files i created, i checked its Standard SOP Classes is Enhanced CT Image Storage , there shouldn’t be the 0028|0030 tag in single frame , but the pixel spacing is still somehow recorded in the multiframe file, so the image could correctly display . Am I right?
If so , how could i read the pixel spacing in code?
And i have also a Ultrasound Multi-frame Image , it has pixel spacing tag for each frame. and i can read it in code. This seems to contradict my conclusion above… :rofl:

Simply reading the image should give you the voxel spacing, available via image->GetSpacing().

2 Likes

with image->GetSpacing() . It works. The solution is so simple but It was a tortuous process. i have learned a lot from this discuss. Thanks for your help!

1 Like

Thanks for telling me about the mechanism of the dicom storage class, I learned a lot, thanks again!

1 Like

FYI

https://dicom.innolitics.com/ciods