how to wirte the dicom tags?(update my code)

hello every one,
i am a new user with itk, Here is my question :
how to automatic put some dicom tags in dicom when i turn raw volume data into dicom .?

I wrote some codes to make 3D volumen data in 2d slice , it works , but the 2d-dicom slice does not have any Dicom tags , how can i manually create them and write in dicom?
Anyone knows the solutions or give me some Hits?
here is my code,

Blockquote
int main(int argc, char ** argv)
{
// Verify the number of parameters in the command line
if (argc < 3)
{
std::cerr << "Usage: " << std::endl;
std::cerr << argv[0] << " input3DImageFile output2DImageFile " << std::endl;
std::cerr << " sliceNumber " << std::endl;
return EXIT_FAILURE;
}

using InputPixelType = float;
using OutputPixelType = unsigned short;//if change"short"to float the slice will be better but bigger

using InputImageType = itk::Image<InputPixelType, 3>;// 3D Volume
using OutputImageType = itk::Image<OutputPixelType, 2>;//2d slice

using ReaderType = itk::ImageSeriesReader<InputImageType>;//reader
using WriterType = itk::ImageSeriesWriter<InputImageType, OutputImageType>;//serieswriter

using RAWImageIOType = itk::RawImageIO<InputPixelType,3>;//use rawimageIo because input raw volume
using OUTImageIOType = itk::GDCMImageIO;				//use GDCMImageIO because save as dicom slice

OUTImageIOType::Pointer gdcmImageIo = OUTImageIOType::New();// just pointer
RAWImageIOType::Pointer rawimageio = RAWImageIOType::New();// pointer+1


using NameGeneratorType = itk::NumericSeriesFileNames; //a NameGenerator magic!
using GDICOMTYPE = itk::GDCMSeriesFileNames;			//a SeriesNameGenerator magic!
GDICOMTYPE::Pointer sliceNumberGenerator = GDICOMTYPE::New();//pointer+2
//sliceNumberGenerator->SetUseSeriesDetails(true);
//sliceNumberGenerator->AddSeriesRestriction("0020 | 0013");

////////////////////////////////////////////Set up how to read raw volume///////////////////////////////////
rawimageio->SetDimensions(0, 1289);					
rawimageio->SetDimensions(1, 2617);
rawimageio->SetDimensions(2, 49);

rawimageio->SetOrigin(0, 0.0);           // 图像原点, volume origin
rawimageio->SetOrigin(1, 0.0);
rawimageio->SetOrigin(2, 0.0);

rawimageio->SetSpacing(0, 0.085);			//像素距离,pixel spacing
rawimageio->SetSpacing(1, 0.085);
rawimageio->SetSpacing(2, 1.0);

rawimageio->SetFileTypeToBinary();       // 二进制方式打开, read with Binary method

rawimageio->SetFileDimensionality(2);   // 读取二维数据,read 2 Dimension data
rawimageio->SetByteOrderToLittleEndian();//no idea but it must be here

rawimageio->SetNumberOfComponents(1);   // 单个像素的位数
rawimageio->SetPixelType(itk::ImageIOBase::SCALAR); // 像素类型
rawimageio->SetComponentType(itk::ImageIOBase::FLOAT); // 像素类型
rawimageio->SetHeaderSize(0);//just data volume no header in raw ,so set 0



////////////////////////////////////////////Set up how to write raw volume///////////////////////////////////
gdcmImageIo->SetSpacing(0, 0.085);  
gdcmImageIo->SetSpacing(1, 0.085);
gdcmImageIo->SetNumberOfComponents(1);   // 单个像素的位数
gdcmImageIo->SetPixelType(itk::ImageIOBase::SCALAR); // 像素类型
gdcmImageIo->SetLoadPrivateTagsDefault;



////////////////////////////////////////////Set up the Read/write Directory///////////////////////////////////

const char * inputFilename = argv[1];//"F:/ITKTrz/2020401reader/Volume00000_float_1289x2617x49.raw";
const char * outputDirectory = argv[2];//"F:/ITKTrz/2020401reader/";
itksys::SystemTools::MakeDirectory(outputDirectory);//if not exist the outputDirectory then create 

//sliceNumberGenerator->SetOutputDirectory(outputDirectory);//outputDirectory

ReaderType::Pointer reader = ReaderType::New();//pointer
WriterType::Pointer writer = WriterType::New();//pointer
NameGeneratorType::Pointer nameGenerator = NameGeneratorType::New();//point
reader->SetFileName(inputFilename);//set up
reader->SetImageIO(rawimageio);//set up


////////////////////////////////////////////NameGenerator magic the name part!///////////////////////////////////
std::string format = "DICOM_";
std::string formatSlice = "%03d.";
std::string formatExtension = ".ima"; // filename extension
format = std::string(outputDirectory) +format+ formatSlice+formatExtension;// user can change the format of the dicom slice
nameGenerator->SetSeriesFormat(format.c_str());


//////////////////////////////////I wanna automatic set dicom header for each dicom slice but not work ,it will be all the same dicom header /////////////////////////////////////////////////////////////////////////////
int AcquisitionNumber = 1;
int InstanceNumber = 0;
int ImagePositionPatient[3] = { 111,111,11 };
int SliceLocation = 0;
itk::MetaDataDictionary & dict = gdcmImageIo->GetMetaDataDictionary();//MetaDataDictionary is about dicom header
//using itr = itk::MetaDataDictionary::Iterator;
std::string tagkey;
std::string value;
//std::string value2 = std::to_string(start[2]);
tagkey = "0020|0012";
value = "value2";//"Acquisition Number	";
itk::EncapsulateMetaData<std::string>(dict, tagkey, value);
tagkey = "0020|0013";
value = InstanceNumber++;//"Instance Number";
itk::EncapsulateMetaData<std::string>(dict, tagkey, value);
tagkey = "0020|0032";
value = "Image Position Patient";
itk::EncapsulateMetaData<std::string>(dict, tagkey, value);
tagkey = "0020|1041";
value = InstanceNumber++;//" Slice Location";
itk::EncapsulateMetaData<std::string>(dict, tagkey, value);
tagkey = "0008|0060";
value = "CT";//" Slice Location";
itk::EncapsulateMetaData<std::string>(dict, tagkey, value);
tagkey = "0020|000D";
value = "1.3.12.2.1107.5.12.7.310.30000020033006414815200000004";//" Slice Location";
itk::EncapsulateMetaData<std::string>(dict, tagkey, value);
tagkey = "0020|000E";
value = "1.3.12.2.1107.5.12.7.310.30000020033006414818900000667";//" Series Instance UID ";

reader->SetMetaDataDictionary(dict);

try
{
	reader->Update();
}
catch ( itk::ExceptionObject & excp)
{
	std::cerr << "Exception thrown while reading the image" << std::endl;
	std::cerr << excp << std::endl;
}

////////////////////////////////////////////NameGenerator magic!how to extract slice from volume///////////////////////////////////

InputImageType::Pointer inputImage = reader->GetOutput();
InputImageType::RegionType   region = inputImage->GetLargestPossibleRegion();
InputImageType::IndexType    start = region.GetIndex();
InputImageType::SizeType     size = region.GetSize();

const unsigned int firstSlice = start[2];
const unsigned int lastSlice = start[2] + size[2] - 1;
nameGenerator->SetStartIndex(firstSlice);
nameGenerator->SetEndIndex(lastSlice);
nameGenerator->SetIncrementIndex(1);

writer->SetFileNames(nameGenerator->GetFileNames());//SetFileNames
writer->SetImageIO(gdcmImageIo);
writer->SetInput(reader->GetOutput());
try
{
	writer->Update();
}
catch (itk::ExceptionObject & err)
{
	std::cerr << "ExceptionObject caught !" << std::endl;
	std::cerr << err << std::endl;
	return EXIT_FAILURE;
}

Thanks!!!

Try instead: writer->SetMetaDataDictionary(dict);

Thank your answer!! but it didn`t work when i change the “reader” to “writer”.

I think the problem is that i used “ImageSeriesWriter” and “nameGenerator” ,while the “SeriesWriter” can in one-time automatic save/extract all the slices and save their information (or not explicit, at least there is no iterator in my code). therefore dicom Tags part can not update himself.

I want every Slice at least has his own “Acquisition Number”,for example first slice should be 0 then second slice is 1 and so on…

Try to use:
writer->SetMetaDataDictionaryArray(mdictArr);
mdictArr is a vector of metadata dictionaries for each slice.
This example may help.

2 Likes

Thank you so much!sorry for so late reply , these days i was working on other things. this example is very inspiring ! But in this example the Tags of new Slice is copy from the olds. Still, there’s a lot of useful functions and classes in there that I didn’t know about before. I will try it later . Thank you again