Live stream of raw data to dicom file

All other questions regarding DCMTK

Moderator: Moderator Team

Post Reply
Message
Author
Bartek
Posts: 6
Joined: Wed, 2016-07-27, 16:51

Live stream of raw data to dicom file

#1 Post by Bartek »

Hi everyone,

I already read some topics on this forum but I don't have explicit answer for my problem.
The goal what I would like to achieve is saving data(images) in real time from "instrument/camera it doesn't matter" to DICOM file. Data are represented as byte array(RAW data).
Number of frames at the beginning experiment is unknown. So I need to do it dynamically. And very important thing is that amount of data is very large per one file(more than 30GB)

1) First of all, is it possible using DICOM and dcmtk library?
2) I'm notice, when I use sample code from this forum about saving uncompressed RAW data, the maximum file size is around 4GB. To summarize this point, when amount of data is less than 4GB file is saved correctly, and I can open this file in other DICOM viewer. Otherwise, file is corrupted. Below I put code which I use.

Code: Select all

        DcmFileFormat fileformat;	
	DcmDataset *dataset = fileformat.getDataset();
	char uid[100];
	dataset->putAndInsertString(DCM_SOPClassUID, UID_BasicGrayscaleImageBoxSOPClass);
	dataset->putAndInsertString(DCM_SOPInstanceUID, dcmGenerateUniqueIdentifier(uid, SITE_INSTANCE_UID_ROOT));
	dataset->putAndInsertString(DCM_PatientName, "SomeName");
	dataset->putAndInsertString(DCM_PatientID, "1234 ");
	dataset->putAndInsertString(DCM_PatientBirthDate, "19010101");
	dataset->putAndInsertString(DCM_StudyDate, date.c_str());
	dataset->putAndInsertString(DCM_StudyTime, time.c_str());
	dataset->putAndInsertString(DCM_StudyDescription, "Tests");
	dataset->putAndInsertString(DCM_NumberOfFrames, std::to_string(numberOfImages).c_str());
	dataset->putAndInsertUint16(DCM_Rows, height);
	dataset->putAndInsertUint16(DCM_Columns, width);
	dataset->putAndInsertUint16(DCM_BitsAllocated, 8);
	dataset->putAndInsertUint16(DCM_BitsStored, 8);
	dataset->putAndInsertUint16(DCM_PhysicalUnitsXDirection, 0x000C);
	dataset->putAndInsertUint16(DCM_PhysicalUnitsYDirection, 0x000C);
	dataset->putAndInsertUint16(DCM_PixelRepresentation, 0 /* 0 - unsigned, 1 - signed*/);	
	dataset->putAndInsertString(DCM_PhotometricInterpretation, "MONOCHROME2");

	auto pData = new BYTE[(long long)width*height*numberOfImages];	
	OFCondition status = dataset->putAndInsertUint8Array(DCM_PixelData, pData, width*height*numberOfImages, false);
	status = fileformat.saveFile("e:\\test.dcm", EXS_LittleEndianExplicit);
3) Is this possible to save multiple series to one DICOM file? If so, where can I find samples?
4) Is there any performance test(benchmarks) to save dicom files to hard drive using dcmtk?

Thank you in advice!
Last edited by Bartek on Thu, 2016-07-28, 19:52, edited 1 time in total.

Michael Onken
DCMTK Developer
Posts: 2048
Joined: Fri, 2004-11-05, 13:47
Location: Oldenburg, Germany
Contact:

Re: Live stream raw data to dicom file

#2 Post by Michael Onken »

Hi,

ad 1 and 2)

Uncompressed pixel data in DICOM is stored in a single blob with all frames stored directly next to each other from frame 0 to frame n in the DICOM element Pixel Data.

In DCMTK you usually set the whole pixel data (including all frames) with a single call, usually something like putAndInsertUint8Array() which takes an array and the array length as parameters, just like your examples demonstrates it. To create such an array you must know the size beforehand, or copy every time the array "grows" (i.e. to adapt dynamically for the frames that come in). Actually DCMTK even copies the given array, so you would have every frame two times in memory if you call putAndInsertUint8Array().

Thus, creating a DICOM file in memory with adding frames dynamically would be very inefficient because of copying the array (on your user side) and having a copy in memory when inserting.

Also, DICOM Pixel Data in uncompressed format can not exceed 4 GB. This is since the length field of each DICOM data element is 4 bytes (for the Pixel Data element) so the maximum value for the length field is 32^2-1 bytes. So there is no way of storing 30 GB in native (i.e. uncompressed) pixel format.

However, uncompressed data is stored a bit differently in DICOM. The pixel data of all frames still goes into element Pixel Data. However, the length field is set to "unknown" (all bits set to 1). That makes it possible to exceed the 4 GB limit for the Pixel Data element. Within the element, a pixel sequence, which is kind of a DIOCM pseudo sequence, exists, with every item usually representing 1 frame. The first pixel item is special and contains an offset table, with offsets to every frame

DCMTK should allow you to dynamically create files with compressed pixel data by adding frame by frame to the pixel sequence. Look into dcmdata/libi2d/i2d.cc, method insertEncapsulatedPixelData(). Basically you create the PixelSequence, add the offset table and then add frame after frame (i2d.cc only copes with a single frame).

After you have all your frames set, you should set the Number of Frames attribute in the main dataset (depending on the SOP Class there also may be other attributes of course).

Note that such a huge objects can be created in theory, but I doubt that most existing tools would be able to deal with them. Also, you can not decompress them (i.e. change the transfer syntax) to uncompressed DICOM, a task that is often required if a client asks the PACS for uncompressed pixel data since it does not understand the corresponding compressed transfer syntax.

add 3) You cannot store more than a single object within one DICOM object (i.e.instance, file). However, and that could also be interesting for your image with thousands of frames, you can split a single DICOM object into several ones, a feature in DICOM called "Concatenations". Viewers that support that feature will recognize that those multiple files then present the user a single, unified set of frames.

add 4) Not that I would know.

Best,
Michael

Bartek
Posts: 6
Joined: Wed, 2016-07-27, 16:51

Re: Live stream raw data to dicom file

#3 Post by Bartek »

Also, DICOM Pixel Data in uncompressed format can not exceed 4 GB. This is since the length field of each DICOM data element is 4 bytes (for the Pixel Data element) so the maximum value for the length field is 32^2-1 bytes. So there is no way of storing 30 GB in native (i.e. uncompressed) pixel format.
Just in case. It should be 2^32 - 1 byte.
However, uncompressed data is stored a bit differently in DICOM. The pixel data of all frames still goes into element Pixel Data. However, the length field is set to "unknown" (all bits set to 1). That makes it possible to exceed the 4 GB limit for the Pixel Data element. Within the element, a pixel sequence, which is kind of a DIOCM pseudo sequence, exists, with every item usually representing 1 frame. The first pixel item is special and contains an offset table, with offsets to every frame
So, it seems to be exactly what I need! But I have one concern about this. The file will be prepared and stored in RAM, until it will explicitly call "save method". So, if I understand correctly this solution have limitation by computer RAM size. Is it correct? Is there saving method which put data directly to hard drive?

I looked for your example file. I try to use this approach, but is not working. Could you look at below code?

Code: Select all

DcmFileFormat ff;
	DcmDataset *dset = ff.getDataset();
	OFCondition cond;

	dset->putAndInsertString(DCM_SOPClassUID, UID_SecondaryCaptureImageStorage);
	dset->putAndInsertString(DCM_SOPInstanceUID, dcmGenerateUniqueIdentifier(uid, SITE_INSTANCE_UID_ROOT));
	dset->putAndInsertString(DCM_PatientName, "SomeTest ");
	dset->putAndInsertString(DCM_PatientID, "1234 ");
	dset->putAndInsertString(DCM_PatientBirthDate, "19010101");
	dset->putAndInsertString(DCM_StudyDate, date.c_str());
	dset->putAndInsertString(DCM_StudyTime, time.c_str());
	dset->putAndInsertString(DCM_StudyDescription, "Test slices");	
	dset->putAndInsertString(DCM_SeriesDescription, "...");	
	dset->putAndInsertString(DCM_NumberOfFrames, std::to_string(numberOfImages).c_str());	
	dset->putAndInsertUint16(DCM_Rows, height);
	dset->putAndInsertUint16(DCM_Columns, width);
	dset->putAndInsertUint16(DCM_BitsAllocated, 8);
	dset->putAndInsertUint16(DCM_BitsStored, 8);
	dset->putAndInsertUint16(DCM_PhysicalUnitsXDirection, 0x000C);
	dset->putAndInsertUint16(DCM_PhysicalUnitsYDirection, 0x000C);
	dset->putAndInsertUint16(DCM_PixelRepresentation, 0 /* 0 - unsigned, 1 - signed*/);
	dset->putAndInsertString(DCM_PhotometricInterpretation, "MONOCHROME2");
		
	// create initial pixel sequence
	DcmPixelSequence* pixelSequence = new DcmPixelSequence(DcmTag(DCM_PixelData, EVR_OB));
	// insert empty offset table into sequence
	DcmPixelItem *offsetTable = new DcmPixelItem(DcmTag(DCM_Item, EVR_OB));
	cond = pixelSequence->insert(offsetTable);

	DcmOffsetList dummyList;
	for (int i = 0; i < numberOfImages; ++i)
	{
		//This part is from implementation.
		//cond = pixelSequence->storeCompressedFrame(dummyList, (Uint8*)pData, width*height, 0); 

		auto item = new DcmPixelItem(DcmTag(DCM_Item, EVR_OB));
		item->putUint8Array(pData, width*height); //the same pointer for data. just for test	
		cond = pixelSequence->insert(item);	
		if (cond.bad())
		{
			break;
		}		
	}
	// insert pixel data attribute incorporating pixel sequence into dataset
	DcmPixelData *pixelData = new DcmPixelData(DCM_PixelData);
	pixelData->putOriginalRepresentation(EXS_LittleEndianExplicit, NULL, pixelSequence);
	cond = dset->insert(pixelData);	

	ff.saveFile("e:\\sequence.dcm", EXS_LittleEndianExplicit);
Thank you in advice.

Michael Onken
DCMTK Developer
Posts: 2048
Joined: Fri, 2004-11-05, 13:47
Location: Oldenburg, Germany
Contact:

Re: Live stream of raw data to dicom file

#4 Post by Michael Onken »

Hi,

Yes, everything is stored in RAM.

There is a function on DcmElement (e.g. representing the Pixel Data element) called createValueFromTempFile. It is used in mdfdsman.cc (which belongs to the commandline tool dcmodify). I *think* when saving the dataset later, the value from the file is directly copied to the destination DICOM file you save to without loading it into RAM (if I remember correctly...).

So you could collect your frames on disk in a file whose format mirrors the binary content of a compressed Pixel Data attribute with its pixel items... and in the end call createValueFromTempFile() and pointing it to the file. I'm not convinced that this will work very well but it seems to be possible...

Regarding your source code: Did you try storeCompressedFrame() that is outcommented in the code? What was the problem?

Actually I wonder why you do not collect all frames on disk and then create the complete DICOM file afterwards besides you probably can ;)

Best,
Michael

P.S: Sure, 2^32-1 8)

J. Riesmeier
DCMTK Developer
Posts: 2501
Joined: Tue, 2011-05-03, 14:38
Location: Oldenburg, Germany
Contact:

Re: Live stream of raw data to dicom file

#5 Post by J. Riesmeier »

So you could collect your frames on disk in a file whose format mirrors the binary content of a compressed Pixel Data attribute with its pixel items... and in the end call createValueFromTempFile() and pointing it to the file. I'm not convinced that this will work very well but it seems to be possible...
Yes, it actually works (quite well). I used this for one of my customer projects.

Bartek
Posts: 6
Joined: Wed, 2016-07-27, 16:51

Re: Live stream of raw data to dicom file

#6 Post by Bartek »

Yes, everything is stored in RAM.
Hm... I wonder what is the reason of this approach? This is just DCMTK implementation, or DICOM standard have some limitation that you have to store everything in RAM?
There is a function on DcmElement (e.g. representing the Pixel Data element) called createValueFromTempFile. It is used in mdfdsman.cc (which belongs to the commandline tool dcmodify). I *think* when saving the dataset later, the value from the file is directly copied to the destination DICOM file you save to without loading it into RAM (if I remember correctly...).

So you could collect your frames on disk in a file whose format mirrors the binary content of a compressed Pixel Data attribute with its pixel items... and in the end call createValueFromTempFile() and pointing it to the file. I'm not convinced that this will work very well but it seems to be possible...
I'm understand this approach, but... is extremely NOT efficient way to do anything.
Regarding your source code: Did you try storeCompressedFrame() that is outcommented in the code? What was the problem?
In case when I directly use code from example, I got file which have size ~1KB, so it seems to be corrupted. Modified version(the same what I presented), generate file at size ~600KB(it is also corrupted). When I use approach from my first post, the generated file have 63.5 MB, and is readable in DICOM viewer. Do you have any ideas? Additionally, I would like to confirm something. As I mentioned before, I need to save RAW(unmodified data). So, what is the reason to call storeCompressedFrame()? Maybe my question is stupid... Is this function make compression "on the fly"? I guess not. ;) So, is this function require pointer to compressed data? Or...?
So you could collect your frames on disk in a file whose format mirrors the binary content of a compressed Pixel Data attribute with its pixel items... and in the end call createValueFromTempFile() and pointing it to the file. I'm not convinced that this will work very well but it seems to be possible...
Yes, it actually works (quite well). I used this for one of my customer projects.
As I mentioned before I understand this approach, but for my case, it seems to be workaround.

Thank you for support!

Michael Onken
DCMTK Developer
Posts: 2048
Joined: Fri, 2004-11-05, 13:47
Location: Oldenburg, Germany
Contact:

Re: Live stream of raw data to dicom file

#7 Post by Michael Onken »

Hi Bartek,
Bartek wrote:
Yes, everything is stored in RAM.
Hm... I wonder what is the reason of this approach? This is just DCMTK implementation, or DICOM standard have some limitation that you have to store everything in RAM?
Well, I think it's just the straight-forward approach. What do you have in mind instead, allowing to cache elements to temporary files starting at a certain threshold?

Also note that regular DICOM files are not bigger than 4 GB which is no problem for current systems so there are no problems with the most common cases (apart from DICOM pathology files, for example), and corner cases where people want to create huge compressed images... (where Concatenations may be the better approach and more interoperable).

By the way, when loading from a file, element values are only accessed/read on access if they are larger than a configurable threshold. Also there is an API for reading partial values.
There is a function on DcmElement (e.g. representing the Pixel Data element) called createValueFromTempFile. It is used in mdfdsman.cc (which belongs to the commandline tool dcmodify). I *think* when saving the dataset later, the value from the file is directly copied to the destination DICOM file you save to without loading it into RAM (if I remember correctly...).

So you could collect your frames on disk in a file whose format mirrors the binary content of a compressed Pixel Data attribute with its pixel items... and in the end call createValueFromTempFile() and pointing it to the file. I'm not convinced that this will work very well but it seems to be possible...
I'm understand this approach, but... is extremely NOT efficient way to do anything.
What kind of API do you prefer instead?
Regarding your source code: Did you try storeCompressedFrame() that is outcommented in the code? What was the problem?
In case when I directly use code from example, I got file which have size ~1KB, so it seems to be corrupted. Modified version(the same what I presented), generate file at size ~600KB(it is also corrupted). When I use approach from my first post, the generated file have 63.5 MB, and is readable in DICOM viewer. Do you have any ideas? Additionally, I would like to confirm something. As I mentioned before, I need to save RAW(unmodified data). So, what is the reason to call storeCompressedFrame()? Maybe my question is stupid... Is this function make compression "on the fly"? I guess not. ;) So, is this function require pointer to compressed data? Or...?
No ideas, sorry... maybe step through img2dcm source code with the debugger to see which function calls you might have missed. The function requires compressed data as determined by the transfer syntax you like to use , e.g. a JPEG lossy image.
So you could collect your frames on disk in a file whose format mirrors the binary content of a compressed Pixel Data attribute with its pixel items... and in the end call createValueFromTempFile() and pointing it to the file. I'm not convinced that this will work very well but it seems to be possible...
Yes, it actually works (quite well). I used this for one of my customer projects.
As I mentioned before I understand this approach, but for my case, it seems to be workaround.

Thank you for support!
You're welcome, best regards
Michael

Bartek
Posts: 6
Joined: Wed, 2016-07-27, 16:51

Re: Live stream of raw data to dicom file

#8 Post by Bartek »

Also note that regular DICOM files are not bigger than 4 GB which is no problem for current systems so there are no problems with the most common cases (apart from DICOM pathology files, for example), and corner cases where people want to create huge compressed images... (where Concatenations may be the better approach and more interoperable).
For now, probably it is true. But technology is growing so fast now, that 4GB will be definitely not enough in near future. As you can see, it is not enough right now for our approach.
Well, I think it's just the straight-forward approach. What do you have in mind instead, allowing to cache elements to temporary files starting at a certain threshold?
...
What kind of API do you prefer instead?
API can be almost the same. But implementation can be different.

Code: Select all

DcmFileFormat file;
In here we can assume that user want to create file, so instead creating "memory stream", you can open file stream, and every operation make directly on hard disk. So it means, that each next operation will operate on hard disk structure instead on memory stream. If users want to create DataSet in memory then, they need to call DcmDataset constructor directly, and next operate on memory stream. For me it is more intuitive.
Of course DcmFileFormat class, should be changed a little bit...
Performance is next important aspect of this approach. If user wants to manipulate data during "creation", storing data in memory maybe better way. But, if you want just save a data to disk, "my" approach should be more efficient. Today's hard disks are very fast. SSD disk allow to write data with ~1,2GB/s, so in my opinion is it not a bottleneck for this solution.
Do you heard about HDF5 file format? It seems to be good approach for modern high performance, large storage data sets.
No ideas, sorry... maybe step through img2dcm source code with the debugger to see which function calls you might have missed. The function requires compressed data as determined by the transfer syntax you like to use , e.g. a JPEG lossy image.
I would like summarize our conversation so far. If I need to save huge amount of RAW data, then I have to use approach form my first post. It means, that I have to make large buffer which is less than 4GB, put images one by one into, a then save data to disk. 4GB limitation is defined by DICOM standard, because standard use 4byte(32 bits) filed, so the maximum size which we can use is defined by 2^32 -1 byte. Is it correct so far?
The only way to store data which size is more than 4GB, is to compress data, and use "sequence" approach. This way is allow us to make file without any limitation, but data must be compressed. Additionally, this approach can met difficulty with reading on "DICOM vierwers". Is it correct?


Thank you,
Bartek.

Jan Schlamelcher
OFFIS DICOM Team
OFFIS DICOM Team
Posts: 318
Joined: Mon, 2014-03-03, 09:51
Location: Oldenburg, Germany

Re: Live stream of raw data to dicom file

#9 Post by Jan Schlamelcher »

Hi,

theoretically speaking, I would propose a solution based on memory mapped IO, i.e. based on boost Memory Mapped Files. This hands the caching decisions where they belong: the operating system (or boost, in case the OS does not provide sufficient facilities).
Practically speaking, we do not depend on boost and do not plan to do so, and I don't really see a chance that this might be implemented any time in the near future except for as an experimental DCMTK plugin or something like that.

Best regards
Jan

Bartek
Posts: 6
Joined: Wed, 2016-07-27, 16:51

Re: Live stream of raw data to dicom file

#10 Post by Bartek »

I see. I would like to be sure, could you confirm my questions from my previous post?

Thanks,
Bartek.

Michael Onken
DCMTK Developer
Posts: 2048
Joined: Fri, 2004-11-05, 13:47
Location: Oldenburg, Germany
Contact:

Re: Live stream of raw data to dicom file

#11 Post by Michael Onken »

Hi Bartek,

Yes your summary is all correct!

Also I like the DcmFileFormat idea, but probably not very high on our priority list.. M
I have heard about HDF5 but don't know any details.

Sorry for late response,
Michael

Bartek
Posts: 6
Joined: Wed, 2016-07-27, 16:51

Re: Live stream of raw data to dicom file

#12 Post by Bartek »

Unfortunately, it means that I need to use another format.

Thank you very much for support.
Take care guys!

Best regards,
Bartek

Michael Onken
DCMTK Developer
Posts: 2048
Joined: Fri, 2004-11-05, 13:47
Location: Oldenburg, Germany
Contact:

Re: Live stream of raw data to dicom file

#13 Post by Michael Onken »

Good luck for your project :)

Post Reply

Who is online

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