How to save one instance per file?

All other questions regarding DCMTK

Moderator: Moderator Team

Post Reply
Message
Author
Marina.Beguin
Posts: 5
Joined: Thu, 2022-02-03, 08:45

How to save one instance per file?

#1 Post by Marina.Beguin »

Hi Dicom experts!

I am pretty new to DICOM and I have a problem to create and save different instances in different files.

I have 200 pixels in the z direction and I would like to save one slice (=instance) per file, one slice being a pixel plan of the z axis.
In file image_0.dcm, I expect to have the image of the XY plan at z=0.
In file image_100.dcm, I expect to have the image of the XY plan at z=100, and so on
However, I have exactly the same image saved in each .dcm file.

My first question is: is it the right way to do? It is what I understood from the DICOM format documentation but I am not 100% sure.

Hereafter are a few lines of my code showing how I saved my files and a few general mandatory definitions. Some of the DICOM mandatory parameters are not written, I did it on purpose to not overload the example. Let me know if you need a parameter description that is not shown below.

I probably described something general to every instance while it should be different for each of them. Could you please give me a feedback on my settings?

Code: Select all


	// Mandatory - Defined for every instances
	dataset->putAndInsertString(DCM_SOPClassUID, "1.2.840.10008.5.1.4.1.1.128");
	dataset->putAndInsertString(DCM_SOPInstanceUID, dcmGenerateUniqueIdentifier(uid, SITE_INSTANCE_UID_ROOT));
	dataset->putAndInsertString(DCM_StudyInstanceUID, dcmGenerateUniqueIdentifier(uid, SITE_INSTANCE_UID_ROOT));
	dataset->putAndInsertString(DCM_SeriesInstanceUID, dcmGenerateUniqueIdentifier(uid, SITE_INSTANCE_UID_ROOT));
	dataset->putAndInsertString(DCM_FrameOfReferenceUID, dcmGenerateUniqueIdentifier(uid, SITE_INSTANCE_UID_ROOT));
	dataset->putAndInsertString(DCM_PositionReferenceIndicator, "");

	// loop over each instance
       for(uint z = 0; z < nPixelsZDirection; ++z) {
		std::string file_name = "image_" + std::to_string(z) +".dcm";
		const char* fileName = file_name.c_str();
		
		dataset->putAndInsertString(DCM_InstanceNumber, boost::lexical_cast<std::string>(z).c_str());
        
		dataset->putAndInsertString(DCM_SamplesPerPixel, "1");
		dataset->putAndInsertString(DCM_PhotometricInterpretation, "MONOCHROME2");
		dataset->putAndInsertUint16(DCM_PixelRepresentation, 0);
		dataset->putAndInsertUint16(DCM_Rows, static_cast<Uint16>(nPixelsXDirection));
		dataset->putAndInsertUint16(DCM_Columns, static_cast<Uint16>(nPixelsYDirection));
		dataset->putAndInsertString(DCM_BitsAllocated, "16");
		dataset->putAndInsertString(DCM_BitsStored, "16");
		dataset->putAndInsertString(DCM_HighBit, "15");

		dataset->putAndInsertString(DCM_ImageType, R"(ORIGINAL\PRIMARY)");
		dataset->putAndInsertOFStringArray(DCM_AcquisitionDate, "");
		dataset->putAndInsertOFStringArray(DCM_AcquisitionTime, "");
		dataset->putAndInsertString(DCM_ActualFrameDuration, "");
		dataset->putAndInsertString(DCM_SamplesPerPixel, "1");
		dataset->putAndInsertString(DCM_PhotometricInterpretation, "MONOCHROME2");
		dataset->putAndInsertString(DCM_BitsAllocated, "16");
		dataset->putAndInsertString(DCM_BitsStored, "16");
		dataset->putAndInsertString(DCM_HighBit, "15");
		dataset->putAndInsertString(DCM_RescaleIntercept, "0.00000"); // always 0. for PET images
		dataset->putAndInsertString(DCM_RescaleSlope, "1.00000");
		dataset->putAndInsertString(DCM_FrameReferenceTime, "0.");
		dataset->putAndInsertUint16(DCM_ImageIndex, z);

		dataset->putAndInsertString(DCM_SliceThickness, boost::lexical_cast<std::string>(pixelSizeMM).c_str());
		std::ostringstream sstr;
		sstr.str("");
		sstr << -pixelSizeMM*nPixelsXDirection/2 << R"(\)" << -pixelSizeMM*nPixelsYDirection/2 << R"(\)" << -pixelSizeMM*nPixelsZDirection/2 + z*pixelSizeMM;
		dataset->putAndInsertString(DCM_ImagePositionPatient, sstr.str().c_str());
		sstr.str("");
		sstr << 1 << R"(\)" << 0 << R"(\)" << 0 << R"(\)" << 0 << R"(\)" << 1 << R"(\)" << 0;
		dataset->putAndInsertString(DCM_ImageOrientationPatient, sstr.str().c_str());
		sstr.str("");
 		sstr << pixelSizeMM << R"(\)" << pixelSizeMM;
        	dataset->putAndInsertString(DCM_PixelSpacing, sstr.str().c_str());

		// Convert image bytes to integer, then add to dataset
 		Uint16* pixelData;
 		auto pixelCount = static_cast<unsigned int>(nPixelsXDirection*nPixelsYDirection);
		pixelData = new Uint16[pixelCount];

		int pixelPos = 0;
		for(uint y = 0; y < nPixelsYDirection; ++y) {
			for(uint x = 0; x < nPixelsXDirection; ++x) {
				const int32_t vox = m_gl.getVox(x, y, z);
				pixelData[pixelPos++] = boost::numeric_cast<Uint16>(m_data[vox]);
			}
		}

		dataset->putAndInsertUint16Array(DCM_PixelData, pixelData, pixelCount);

		fileformat.saveFile(fileName, EXS_LittleEndianExplicit);
    }


Marco Eichelberg
OFFIS DICOM Team
OFFIS DICOM Team
Posts: 1437
Joined: Tue, 2004-11-02, 17:22
Location: Oldenburg, Germany
Contact:

Re: How to save one instance per file?

#2 Post by Marco Eichelberg »

One problem that stands out in your code is that you save all files with the same SOP Instance UID. This is something like the world-wide unique serial number of each image and needs to be different for each slice.

Marina.Beguin
Posts: 5
Joined: Thu, 2022-02-03, 08:45

Re: How to save one instance per file?

#3 Post by Marina.Beguin »

Thanks a lot for helping.

I had some doubt the error come from that. However I tried to generate one SOP Instance UID per file but I go the following error message:
W: DcmFileFormat: Value of SOPInstanceUID in MetaHeader and Dataset is different
W: DcmFileFormat: Value of SOPInstanceUID in MetaHeader and Dataset is different
W: DcmFileFormat: Value of SOPInstanceUID in MetaHeader and Dataset is different
W: DcmFileFormat: Value of SOPInstanceUID in MetaHeader and Dataset is different
W: DcmFileFormat: Value of SOPInstanceUID in MetaHeader and Dataset is different
W: DcmFileFormat: Value of SOPInstanceUID in MetaHeader and Dataset is different
W: DcmFileFormat: Value of SOPInstanceUID in MetaHeader and Dataset is different
.
.
.
Maybe it comes from the way I wrote it. I just did the following:

Code: Select all

	for(uint z = 0; z < nPixelsZDirection; ++z) {
		std::string file_name = "image_" + std::to_string(z) +".dcm";
		const char* fileName = file_name.c_str();
		
		char uid[100];
[b]        	dataset->putAndInsertString(DCM_SOPInstanceUID, dcmGenerateUniqueIdentifier(uid, SITE_INSTANCE_UID_ROOT));[/b]

		dataset->putAndInsertString(DCM_InstanceNumber, boost::lexical_cast<std::string>(z).c_str());
		.
		.
		.
	}
On the forum I found a way to remove the MetaHeader error message:

Code: Select all

        DcmItem *metaInfo = m_fileformat.getMetaInfo();
        if (metaInfo)
        {
        // force the meta-header UIDs to be re-generated when storing the file 
        // since the UIDs in the data set may have changed 
        delete metaInfo->remove(DCM_MediaStorageSOPClassUID);
        delete metaInfo->remove(DCM_MediaStorageSOPInstanceUID);
        }
If I do this, the error message do not appear, but I have the same problem I had initially: The same image is stored in all files

Is there a way to increment the UID generated by dcmGenerateUniqueIdentifier(uid, SITE_INSTANCE_UID_ROOT) maybe? I also tried that:

Code: Select all

        dataset->putAndInsertString(DCM_SOPInstanceUID, dcmGenerateUniqueIdentifier(uid, SITE_INSTANCE_UID_ROOT + '.' + std::to_string(z)));
but I am not really convinced

Marco Eichelberg
OFFIS DICOM Team
OFFIS DICOM Team
Posts: 1437
Joined: Tue, 2004-11-02, 17:22
Location: Oldenburg, Germany
Contact:

Re: How to save one instance per file?

#4 Post by Marco Eichelberg »

Never manually modify any UID generated by DCMTK's dcmGenerateUniqueIdentifier() function. Simply call the function again to create a new UID. Make sure that you add SOPClassUID and a unique SOPInstanceUID to each dataset you create, and store the file using DcmFileFormat::saveFile, using EWM_createNewMeta for the writeMode parameter. This will create a new meta-header based on the content of the dataset.

Marina.Beguin
Posts: 5
Joined: Thu, 2022-02-03, 08:45

Re: How to save one instance per file?

#5 Post by Marina.Beguin »

Dear Marco,

Thanks a lot for all of these information, I corrected my implementation.
However I still have the same problem. Honestly I tried a lot of thing and I really don't understand what is wrong. It should work as it is...

Hereafter is a print off the parameters for:
instance 197:
# Used TransferSyntax: Little Endian Explicit
(0008,0008) CS [ORIGINAL\PRIMARY] # 16, 2 ImageType
(0008,0016) UI =PositronEmissionTomographyImageStorage # 28, 1 SOPClassUID
(0008,0018) UI [1.2.276.0.7230010.3.1.4.2088271309.3016061.1647341750.853876] # 60, 1 SOPInstanceUID
(0008,0020) DA [20220315] # 8, 1 StudyDate
(0008,0021) DA [20220315] # 8, 1 SeriesDate
(0008,0022) DA (no value available) # 0, 0 AcquisitionDate
(0008,0030) TM [115550] # 6, 1 StudyTime
(0008,0031) TM [115550] # 6, 1 SeriesTime
(0008,0032) TM (no value available) # 0, 0 AcquisitionTime
(0008,0050) SH (no value available) # 0, 0 AccessionNumber
(0008,0060) CS [PT] # 2, 1 Modality
(0008,0070) LO [Me] # 22, 1 Manufacturer
(0008,0090) PN (no value available) # 0, 0 ReferringPhysicianName
(0010,0010) PN [Jonh^Doe] # 10, 1 PatientName
(0010,0020) LO [1234] # 4, 1 PatientID
(0010,0030) DA (no value available) # 0, 0 PatientBirthDate
(0010,0040) CS (no value available) # 0, 0 PatientSex
(0018,0050) DS [0.9375] # 6, 1 SliceThickness
(0018,1181) CS (no value available) # 0, 0 CollimatorType
(0018,1242) IS (no value available) # 0, 0 ActualFrameDuration
(0020,000d) UI [1.2.276.0.7230010.3.1.4.2088271309.3016061.1647341748.853677] # 60, 1 StudyInstanceUID
(0020,000e) UI [1.2.276.0.7230010.3.1.4.2088271309.3016061.1647341748.853678] # 60, 1 SeriesInstanceUID
(0020,0010) SH [00001] # 6, 1 StudyID
(0020,0011) IS [00001] # 6, 1 SeriesNumber
(0020,0013) IS [197] # 4, 1 InstanceNumber
(0020,0032) DS [-46.875\-46.875\90.9375] # 24, 3 ImagePositionPatient
(0020,0037) DS [1\0\0\0\1\0] # 12, 6 ImageOrientationPatient
(0020,0052) UI [1.2.276.0.7230010.3.1.4.2088271309.3016061.1647341748.853676] # 60, 1 FrameOfReferenceUID
(0020,0060) CS (no value available) # 0, 0 Laterality
(0020,1040) LO (no value available) # 0, 0 PositionReferenceIndicator
(0020,1041) DS [90.9375] # 8, 1 SliceLocation
(0028,0002) US 1 # 2, 1 SamplesPerPixel
(0028,0004) CS [MONOCHROME2] # 12, 1 PhotometricInterpretation
(0028,0010) US 100 # 2, 1 Rows
(0028,0011) US 100 # 2, 1 Columns
(0028,0030) DS [0.9375\0.9375] # 14, 2 PixelSpacing
(0028,0051) CS (no value available) # 0, 0 CorrectedImage
(0028,0100) US 16 # 2, 1 BitsAllocated
(0028,0101) US 16 # 2, 1 BitsStored
(0028,0102) US 15 # 2, 1 HighBit
(0028,0103) US 0 # 2, 1 PixelRepresentation
(0028,1052) DS [0.00000] # 8, 1 RescaleIntercept
(0028,1053) DS [1.00000] # 8, 1 RescaleSlope
(0054,0016) SQ (Sequence with explicit length #=1) # 0, 1 RadiopharmaceuticalInformationSequence
(fffe,e000) na (Item with explicit length #=2) # 0, 1 Item
(0018,1078) DT (no value available) # 0, 0 RadiopharmaceuticalStartDateTime
(0054,0300) SQ (Sequence with explicit length #=1) # 0, 1 RadionuclideCodeSequence
(fffe,e000) na (Item with explicit length #=3) # 0, 1 Item
(0008,0100) SH [C-111A1] # 8, 1 CodeValue
(0008,0102) SH [SRT] # 4, 1 CodingSchemeDesignator
(0008,0104) LO [^18^Fluorine] # 12, 1 CodeMeaning
(fffe,e00d) na (ItemDelimitationItem for re-encoding) # 0, 0 ItemDelimitationItem
(fffe,e0dd) na (SequenceDelimitationItem for re-encod.) # 0, 0 SequenceDelimitationItem
(fffe,e00d) na (ItemDelimitationItem for re-encoding) # 0, 0 ItemDelimitationItem
(fffe,e0dd) na (SequenceDelimitationItem for re-encod.) # 0, 0 SequenceDelimitationItem
(0054,0081) US 1 # 2, 1 NumberOfSlices
(0054,0410) SQ (Sequence with explicit length #=1) # 0, 1 PatientOrientationCodeSequence
(fffe,e000) na (Item with explicit length #=3) # 0, 1 Item
(0008,0100) SH [ F-10450] # 8, 1 CodeValue
(0008,0102) SH [99SDM] # 6, 1 CodingSchemeDesignator
(0008,0104) LO [recumbent] # 10, 1 CodeMeaning
(fffe,e00d) na (ItemDelimitationItem for re-encoding) # 0, 0 ItemDelimitationItem
(fffe,e0dd) na (SequenceDelimitationItem for re-encod.) # 0, 0 SequenceDelimitationItem
(0054,0414) SQ (Sequence with explicit length #=1) # 0, 1 PatientGantryRelationshipCodeSequence
(fffe,e000) na (Item with explicit length #=3) # 0, 1 Item
(0008,0100) SH [F-10470] # 8, 1 CodeValue
(0008,0102) SH [99SDM] # 6, 1 CodingSchemeDesignator
(0008,0104) LO [headfirst] # 10, 1 CodeMeaning
(fffe,e00d) na (ItemDelimitationItem for re-encoding) # 0, 0 ItemDelimitationItem
(fffe,e0dd) na (SequenceDelimitationItem for re-encod.) # 0, 0 SequenceDelimitationItem
(0054,1000) CS [STATIC\IMAGE] # 12, 2 SeriesType
(0054,1001) CS [NONE] # 4, 1 Units
(0054,1002) CS [EMISSION] # 8, 1 CountsSource
(0054,1102) CS [NONE] # 4, 1 DecayCorrection
(0054,1300) DS [0.] # 2, 1 FrameReferenceTime
and instance 198:
# Used TransferSyntax: Little Endian Explicit
(0008,0008) CS [ORIGINAL\PRIMARY] # 16, 2 ImageType
(0008,0016) UI =PositronEmissionTomographyImageStorage # 28, 1 SOPClassUID
(0008,0018) UI [1.2.276.0.7230010.3.1.4.2088271309.3016061.1647341750.853877] # 60, 1 SOPInstanceUID
(0008,0020) DA [20220315] # 8, 1 StudyDate
(0008,0021) DA [20220315] # 8, 1 SeriesDate
(0008,0022) DA (no value available) # 0, 0 AcquisitionDate
(0008,0030) TM [115550] # 6, 1 StudyTime
(0008,0031) TM [115550] # 6, 1 SeriesTime
(0008,0032) TM (no value available) # 0, 0 AcquisitionTime
(0008,0050) SH (no value available) # 0, 0 AccessionNumber
(0008,0060) CS [PT] # 2, 1 Modality
(0008,0070) LO [Me] # 22, 1 Manufacturer
(0008,0090) PN (no value available) # 0, 0 ReferringPhysicianName
(0010,0010) PN [John^Doe] # 10, 1 PatientName
(0010,0020) LO [1234] # 4, 1 PatientID
(0010,0030) DA (no value available) # 0, 0 PatientBirthDate
(0010,0040) CS (no value available) # 0, 0 PatientSex
(0018,0050) DS [0.9375] # 6, 1 SliceThickness
(0018,1181) CS (no value available) # 0, 0 CollimatorType
(0018,1242) IS (no value available) # 0, 0 ActualFrameDuration
(0020,000d) UI [1.2.276.0.7230010.3.1.4.2088271309.3016061.1647341748.853677] # 60, 1 StudyInstanceUID
(0020,000e) UI [1.2.276.0.7230010.3.1.4.2088271309.3016061.1647341748.853678] # 60, 1 SeriesInstanceUID
(0020,0010) SH [00001] # 6, 1 StudyID
(0020,0011) IS [00001] # 6, 1 SeriesNumber
(0020,0013) IS [198] # 4, 1 InstanceNumber
(0020,0032) DS [-46.875\-46.875\91.875] # 22, 3 ImagePositionPatient
(0020,0037) DS [1\0\0\0\1\0] # 12, 6 ImageOrientationPatient
(0020,0052) UI [1.2.276.0.7230010.3.1.4.2088271309.3016061.1647341748.853676] # 60, 1 FrameOfReferenceUID
(0020,0060) CS (no value available) # 0, 0 Laterality
(0020,1040) LO (no value available) # 0, 0 PositionReferenceIndicator
(0020,1041) DS [91.875] # 6, 1 SliceLocation
(0028,0002) US 1 # 2, 1 SamplesPerPixel
(0028,0004) CS [MONOCHROME2] # 12, 1 PhotometricInterpretation
(0028,0010) US 100 # 2, 1 Rows
(0028,0011) US 100 # 2, 1 Columns
(0028,0030) DS [0.9375\0.9375] # 14, 2 PixelSpacing
(0028,0051) CS (no value available) # 0, 0 CorrectedImage
(0028,0100) US 16 # 2, 1 BitsAllocated
(0028,0101) US 16 # 2, 1 BitsStored
(0028,0102) US 15 # 2, 1 HighBit
(0028,0103) US 0 # 2, 1 PixelRepresentation
(0028,1052) DS [0.00000] # 8, 1 RescaleIntercept
(0028,1053) DS [1.00000] # 8, 1 RescaleSlope
(0054,0016) SQ (Sequence with explicit length #=1) # 0, 1 RadiopharmaceuticalInformationSequence
(fffe,e000) na (Item with explicit length #=2) # 0, 1 Item
(0018,1078) DT (no value available) # 0, 0 RadiopharmaceuticalStartDateTime
(0054,0300) SQ (Sequence with explicit length #=1) # 0, 1 RadionuclideCodeSequence
(fffe,e000) na (Item with explicit length #=3) # 0, 1 Item
(0008,0100) SH [C-111A1] # 8, 1 CodeValue
(0008,0102) SH [SRT] # 4, 1 CodingSchemeDesignator
(0008,0104) LO [^18^Fluorine] # 12, 1 CodeMeaning
(fffe,e00d) na (ItemDelimitationItem for re-encoding) # 0, 0 ItemDelimitationItem
(fffe,e0dd) na (SequenceDelimitationItem for re-encod.) # 0, 0 SequenceDelimitationItem
(fffe,e00d) na (ItemDelimitationItem for re-encoding) # 0, 0 ItemDelimitationItem
(fffe,e0dd) na (SequenceDelimitationItem for re-encod.) # 0, 0 SequenceDelimitationItem
(0054,0081) US 1 # 2, 1 NumberOfSlices
(0054,0410) SQ (Sequence with explicit length #=1) # 0, 1 PatientOrientationCodeSequence
(fffe,e000) na (Item with explicit length #=3) # 0, 1 Item
(0008,0100) SH [ F-10450] # 8, 1 CodeValue
(0008,0102) SH [99SDM] # 6, 1 CodingSchemeDesignator
(0008,0104) LO [recumbent] # 10, 1 CodeMeaning
(fffe,e00d) na (ItemDelimitationItem for re-encoding) # 0, 0 ItemDelimitationItem
(fffe,e0dd) na (SequenceDelimitationItem for re-encod.) # 0, 0 SequenceDelimitationItem
(0054,0414) SQ (Sequence with explicit length #=1) # 0, 1 PatientGantryRelationshipCodeSequence
(fffe,e000) na (Item with explicit length #=3) # 0, 1 Item
(0008,0100) SH [F-10470] # 8, 1 CodeValue
(0008,0102) SH [99SDM] # 6, 1 CodingSchemeDesignator
(0008,0104) LO [headfirst] # 10, 1 CodeMeaning
(fffe,e00d) na (ItemDelimitationItem for re-encoding) # 0, 0 ItemDelimitationItem
(fffe,e0dd) na (SequenceDelimitationItem for re-encod.) # 0, 0 SequenceDelimitationItem
(0054,1000) CS [STATIC\IMAGE] # 12, 2 SeriesType
(0054,1001) CS [NONE] # 4, 1 Units
(0054,1002) CS [EMISSION] # 8, 1 CountsSource
(0054,1102) CS [NONE] # 4, 1 DecayCorrection
(0054,1300) DS [0.] # 2, 1 FrameReferenceTime

The instanceUID are different and all the other parameters looks good.

Is there a way to close the file into which an instance is saved after saving?
I noticed that if I save only instance 0, the expected image (plan XY at pixel z = 0) is correctly saved. The file contains only instance 0. The more instances are saved, the more instances are added to the same file. It seems the previous instances are kept in memory somewhere.

Hereafter is the code I am using the save my files. Let me know if you notice something wrong.

In the main.cpp

Code: Select all

	DicomFormat dicomImage(vectorOfPixelEntireImage, outputFile);
	dicomImage.saveToDicomFormat();
DicomFormat class

Code: Select all

#include "DicomFormat.h"

#include <boost/lexical_cast.hpp>
#include <boost/numeric/conversion/cast.hpp>


DicomFormat::DicomFormat(const std::vector<float>& data, const std::string outputFileName):
    m_data(data),
    m_outputFile(outputFileName)
{
    dcmGenerateUniqueIdentifier(m_frameUID, SITE_INSTANCE_UID_ROOT);
    dcmGenerateUniqueIdentifier(m_studyUID, SITE_INSTANCE_UID_ROOT);
    dcmGenerateUniqueIdentifier(m_seriesUID, SITE_INSTANCE_UID_ROOT);
}

DicomFormat::~DicomFormat()
{
}

void DicomFormat::saveToDicomFormat()
{
	for(uint z = 0; z < nPixelsZDirection; ++z) { //nInstance = 200 (=200 pixels in z direction)
        std::string file_extension = m_outputFile + "_image_" + std::to_string(z) +".dcm";
        const char* fileName = file_extension.c_str();

        DcmFileFormat fileformat;
        DcmDataset* dataset = fileformat.getDataset();

        setGeneralHeaderParameters(dataset);
        setModulePatientParameters(dataset);
        setModuleStudyParameters(dataset);
        setModuleSeriesParameters(dataset);

        dataset->putAndInsertString(DCM_SOPClassUID, "1.2.840.10008.5.1.4.1.1.128"); //PET image class
        char instanceUID[100];
        dataset->putAndInsertString(DCM_SOPInstanceinstanceUID, dcmGenerateUniqueIdentifier(instanceUID, SITE_INSTANCE_UID_ROOT));

        setModuleImageParameters(dataset, z);
        fileformat.saveFile(fileName, EXS_LittleEndianExplicit, EET_UndefinedLength, EGL_recalcGL, EPD_withoutPadding, 0, 0, EWM_createNewMeta);
    }
}

void DicomFormat::setGeneralHeaderParameters(DcmDataset* dataset)
{
    setModuleFrameOfReferenceParameters(dataset);
    setModuleEquipementParameters(dataset);
}

void DicomFormat::setModuleFrameOfReferenceParameters(DcmDataset* dataset)
{
    // Mandatory Frame of Reference elements
    dataset->putAndInsertString(DCM_FrameOfReferenceUID, m_frameUID);
    dataset->putAndInsertString(DCM_PositionReferenceIndicator, "");
}

void DicomFormat::setModuleEquipementParameters(DcmDataset* dataset)
{
    // Mandatory General equipment elements
    dataset->putAndInsertString(DCM_Manufacturer, "Me");

}

void DicomFormat::setModulePatientParameters(DcmDataset* dataset)
{
    // Mandatory Patient module attributes
    dataset->putAndInsertString(DCM_PatientName, "John^Doe");
    dataset->putAndInsertString(DCM_PatientID, "1234"); //read from the GUI
    dataset->putAndInsertString(DCM_PatientBirthDate, "");
    dataset->putAndInsertString(DCM_PatientSex, "");
}


void DicomFormat::setModuleStudyParameters(DcmDataset* dataset)
{
    // Mandatory General Study elements
    dataset->putAndInsertString(DCM_StudyInstanceUID, m_studyUID);

    OFString s;
    DcmDate::getCurrentDate(s);
    dataset->putAndInsertOFStringArray(DCM_StudyDate, s);
    DcmTime::getCurrentTime(s);
    dataset->putAndInsertOFStringArray(DCM_StudyTime, s);
    dataset->putAndInsertString(DCM_ReferringPhysicianName, ""); // read from the GUI
    dataset->putAndInsertString(DCM_StudyID, "00001"); // study identifier (the clinical trial ID.patientID)
    dataset->putAndInsertOFStringArray(DCM_AccessionNumber, "");
}


void DicomFormat::setModuleSeriesParameters(DcmDataset* dataset)
{
    // Mandatory General Series elements
    dataset->putAndInsertString(DCM_SeriesInstanceUID, m_seriesUID);
    dataset->putAndInsertOFStringArray(DCM_Modality, "PT");
    dataset->putAndInsertString(DCM_SeriesNumber, "00001"); //number that identifies this serie
    dataset->putAndInsertString(DCM_Laterality, "");

    // Mandatory PET Series elements
    OFString s;
    DcmDate::getCurrentDate(s);
    dataset->putAndInsertOFStringArray(DCM_SeriesDate, s); //cannot be empty
    DcmTime::getCurrentTime(s);
    dataset->putAndInsertOFStringArray(DCM_SeriesTime, s); //cannot be empty
    dataset->putAndInsertString(DCM_CollimatorType, "");
    dataset->putAndInsertString(DCM_CorrectedImage, "");
    dataset->putAndInsertUint16(DCM_NumberOfSlices, static_cast<Uint16>(1)); // = number of instances
    dataset->putAndInsertString(DCM_SeriesType, R"(STATIC\IMAGE)");
    dataset->putAndInsertString(DCM_Units, "NONE");
    dataset->putAndInsertString(DCM_CountsSource, "EMISSION");
    dataset->putAndInsertString(DCM_DecayCorrection, "NONE");

    // Mandatory PET isotope elements 
    m_dcm_item = nullptr;
    dataset->findOrCreateSequenceItem(DCM_RadiopharmaceuticalInformationSequence, m_dcm_item);
    m_dcm_item->putAndInsertOFStringArray(DCM_RadiopharmaceuticalStartDateTime, ""); //optionnal but Slicer3D complains if not given
    m_dcm_item->findOrCreateSequenceItem(DCM_RadionuclideCodeSequence, m_dcm_item);
    m_dcm_item->putAndInsertString(DCM_CodeValue, "C-111A1");
    m_dcm_item->putAndInsertString(DCM_CodeMeaning, "^18^Fluorine");
    m_dcm_item->putAndInsertString(DCM_CodingSchemeDesignator, "SRT");

    // Mandatory NM/PET Patient Orientation elements
    m_dcm_item = nullptr;
    dataset->findOrCreateSequenceItem(DCM_PatientOrientationCodeSequence, m_dcm_item);
    m_dcm_item->putAndInsertString(DCM_CodeValue, " F-10450");
    m_dcm_item->putAndInsertString(DCM_CodeMeaning, "recumbent");
    m_dcm_item->putAndInsertString(DCM_CodingSchemeDesignator, "99SDM");
    m_dcm_item = nullptr;
    dataset->findOrCreateSequenceItem(DCM_PatientGantryRelationshipCodeSequence, m_dcm_item);
    m_dcm_item->putAndInsertString(DCM_CodeValue, "F-10470");
    m_dcm_item->putAndInsertString(DCM_CodeMeaning, "headfirst");
    m_dcm_item->putAndInsertString(DCM_CodingSchemeDesignator, "99SDM");
}


void DicomFormat::setModuleImageParameters(DcmDataset* dataset, const uint z)
{
    const double slicePosition = -pixelSize*nPixelsZDirection/2 + z*pixelSize;

    // Mandatory General Image elements
    dataset->putAndInsertString(DCM_InstanceNumber, boost::lexical_cast<std::string>(z).c_str());

    // Mandatory Image plane elements
    dataset->putAndInsertString(DCM_SliceThickness, boost::lexical_cast<std::string>(pixelSize).c_str());
	dataset->putAndInsertString(DCM_SliceLocation, boost::lexical_cast<std::string>(slicePosition).c_str()); //optional

    std::ostringstream sstr;
    sstr.str("");
    sstr << -pixelSize*nPixelsXDirection/2 << R"(\)" << -pixelSize*nPixelYDirection/2 << R"(\)" << slicePosition;
    // sstr << -pixelSize*nPixelsXDirection/2 << R"(\)" << -pixelSize*nPixelYDirection/2;
    dataset->putAndInsertString(DCM_ImagePositionPatient, sstr.str().c_str());
    sstr.str("");
    sstr << 1 << R"(\)" << 0 << R"(\)" << 0 << R"(\)" << 0 << R"(\)" << 1 << R"(\)" << 0;
    dataset->putAndInsertString(DCM_ImageOrientationPatient, sstr.str().c_str());
    sstr.str("");
    sstr << pixelSize << R"(\)" << pixelSize;
    dataset->putAndInsertString(DCM_PixelSpacing, sstr.str().c_str());

    // Mandatory Image pixel elements
    dataset->putAndInsertString(DCM_SamplesPerPixel, "1");
    dataset->putAndInsertString(DCM_PhotometricInterpretation, "MONOCHROME2");
    dataset->putAndInsertUint16(DCM_PixelRepresentation, 0);
    dataset->putAndInsertUint16(DCM_Rows, static_cast<Uint16>(nPixelsXDirection));
    dataset->putAndInsertUint16(DCM_Columns, static_cast<Uint16>(nPixelYDirection));
    dataset->putAndInsertString(DCM_BitsAllocated, "16");
    dataset->putAndInsertString(DCM_BitsStored, "16");
    dataset->putAndInsertString(DCM_HighBit, "15");

    // Mandatory PET Image elements
    dataset->putAndInsertString(DCM_ImageType, R"(ORIGINAL\PRIMARY)");
    dataset->putAndInsertOFStringArray(DCM_AcquisitionDate, "");
    dataset->putAndInsertOFStringArray(DCM_AcquisitionTime, "");
    dataset->putAndInsertString(DCM_ActualFrameDuration, "");
    dataset->putAndInsertString(DCM_RescaleIntercept, "0.00000"); // always 0. for PET images
    dataset->putAndInsertString(DCM_RescaleSlope, "1.00000");
    dataset->putAndInsertString(DCM_FrameReferenceTime, "0.");

    // Pixel Data
    Uint16* pixelData = convertFloatDataIntoUint(z);
    fillPixelDataAttribute(dataset, pixelData);

}


void DicomFormat::fillPixelDataAttribute(DcmDataset* dataset, const Uint16* pixelData)
{
    auto pixelCount = static_cast<unsigned int>(nPixelsXDirection*nPixelYDirection);

    // // first method
    // DcmPixelData* newPixelData = new DcmPixelData(DCM_PixelData);
    // newPixelData->putUint16Array(pixelData, pixelCount);
    // dataset->insert(newPixelData, OFTrue);

    // Second method (same results as first)
    dataset->putAndInsertUint16Array(DCM_PixelData, pixelData, pixelCount);
}


Uint16* DicomFormat::convertFloatDataIntoUint(const uint z)
{
    const int pixelCount = static_cast<int>(nPixelsXDirection*nPixelYDirection);
    std::vector<Uint16> temp(pixelCount, 0);

    int pixelPos = 0;
    for(uint y = 0; y < nPixelYDirection; ++y) {
        for(uint x = 0; x < nPixelXDirection; ++x) {
            const int32_t vox = getVoxID(x, y, z); 
            temp[pixelPos] = boost::numeric_cast<Uint16>(m_data[vox]);
            pixelPos++;
        }
    }

    Uint16* pixelData = new Uint16[pixelCount];
    std::copy(temp.begin(), temp.end(), pixelData);

    return pixelData;
}

Marco Eichelberg
OFFIS DICOM Team
OFFIS DICOM Team
Posts: 1437
Joined: Tue, 2004-11-02, 17:22
Location: Oldenburg, Germany
Contact:

Re: How to save one instance per file?

#6 Post by Marco Eichelberg »

I can see no obvious mistake in the code. DCMTK does not perform any caching, and you create a separate DcmFileFormat instance for each image, which is exactly how things should work.

Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot], Bing [Bot] and 1 guest