DICOM medical image processing: DICOM Storage operation-"storing multiple JPG image data into DCM Files", dicomdcm
Background:
In the previous article, we will continue to introduce how to save multiple JPG images to the DCM file. The lossy compressed data will be directly written into the DCM file and stored in the Multi-frame format.
Save multiple JPG images to the DCM file:
To avoid ambiguity, I would like to explain it here. The scenario described in this blog is: assume that we have multiple JPG files in our hands and want to write the JPG files into the DCM file, that is, a single DCM file contains multiple image information in the Multi-Frame format. This problem was previously discussed with CSDN Boyou y317215133y. At that time, I found a post in the OFFIS forum and gave y317215133y a reply. Today, I reorganized my analysis and found that the situation in my post was slightly different from what I want to describe today: the author in the post already has the raw data of multiple images (from the author's description, the data is not compressed). You want to compress the data series into the DCM file. Presumably, the purpose of this operation is to reduce the storage space, while in this blog, I have JPEG compressed data, that is, I am not trying to reduce the storage space, the goal is to save multiple JPEG images in the Multi-frame DCM format to facilitate archiving management.
The reply provided by the offis dicom Team in the post is:1)Create a DcmFileFormat object and use getDataset () to obtain the data body pointer;2)Use putAndInsertXXX to write non-compressed raw image data to Dataset in 1), that is, DICOM medical image processing in the previous blog: DICOM Storage operation-the method used to "store multiple BMP image data into DCM Files;3)Register JPEG encoding parameters, such as DJ_PRLossless and DJ_RPLossy, and then call the chooseRepresentation function. This operation is to perform JPEG lossy or lossless compression on the DCM file. For details, refer to the Code in dcmcjpeg. cc;4)Call the saveFile function to write the encoded data to the Multi-fram DCM file.
The DcmPixelSequence class is not used for the above four steps. In this scenario, the post author and Boyou y317215133y want to use DcmPixelSequence to learn about writing SQ fields, the DcmPixelSequence class is incorrectly used only when the scenario is incorrect. The post author also gave a prompt, such:
The above is exactly what we need to do in this article. We hope to use this example to explain how to use the DcmPixelSequence class and learn more about JPEG-compressed Multi-frame DCM files.
Code example:
Refer to the Code http://forum.dcmtk.org/viewtopic.php in the offis forum? T = 1544 & highlight = creating + multiframe + dicom + images, directly provide the source code:
// DcmPixelDataTest.cpp: defines the entry point of the console application.
//
#include "stdafx.h"
#include "dcmtk / config / osconfig.h"
#include "dcmtk / dcmdata / dctk.h"
#include "dcmtk / dcmdata / dcistrmf.h"
#include "dcmtk / dcmdata / dcpixel.h"
#include "dcmtk / dcmdata / dcpixseq.h"
#include "dcmtk / dcmdata / dcpxitem.h"
/ * ---- BMP image analysis ---- * /
#include "dcmtk / dcmdata / libi2d / i2dbmps.h"
#include "DicomUtils.h"
/ * ---- JPEG image analysis ---- * /
#include "dcmtk / dcmdata / libi2d / i2djpgs.h"
#include "dcmtk / dcmdata / libi2d / i2doutpl.h"
#include "dcmtk / dcmdata / dcerror.h"
#include <direct.h>
int _tmain (int argc, _TCHAR * argv [])
{
OFCondition status;
DcmFileFormat fileformat;
DcmDataset * mydatasete = fileformat.getDataset ();
DicomUtils :: AddDicomElements ((DcmDataset * &) mydatasete);
Uint16 rows, cols, samplePerPixel, bitsAlloc, bitStord, highBit, pixelRpr, planConf, pixAspectH, pixAspectV
OFString photoMetrInt;
Uint32 length;
E_TransferSyntax ts;
char curDir [255];
getcwd (curDir, 255);
DcmPixelSequence * seq = new DcmPixelSequence (DcmTag (DCM_PixelData, EVR_OB));
/ *! ------ zssure: begin, add an empty Dicom Pixel Item as Offset Fragment ------! * /
// seq-> insert (new DcmPixelItem (DcmTag (DCM_Item, EVR_OB)));
/ * ------- zssure: end, can successfully solve the problem of storing multiple JPEGs in DCM ------------------------- * /
// Cycle to add 4 pictures
for (int i = 0; i <4; ++ i)
{
OFString num;
char numtmp [255];
memset (numtmp, 0, sizeof (char) * 255);
sprintf (numtmp, "% s \\ jpeg-test \\% d.jpg", curDir, i + 1);
OFString filename = OFString (numtmp);
I2DJpegSource * bmpSource = new I2DJpegSource ();
bmpSource-> setImageFile (filename);
char * pixData = NULL;
bmpSource-> readPixelData (rows, cols, samplePerPixel, photoMetrInt, bitsAlloc, bitsStored, highBit, pixelRpr, planConf, pixAspectH, pixAspectV, pixData, length, ts);
DcmPixelItem * newItem = new DcmPixelItem (DcmTag (DCM_Item, EVR_OB));
if (newItem! = NULL)
{
seq-> insert (newItem);
OFCondition result = newItem-> putUint8Array ((Uint8 *) pixData, length);
}
delete bmpSource;
};
mydatasete-> putAndInsertUint16 (DCM_SamplesPerPixel, samplePerPixel);
mydatasete-> putAndInsertString (DCM_NumberOfFrames, "4");
mydatasete-> putAndInsertUint16 (DCM_Rows, rows);
mydatasete-> putAndInsertUint16 (DCM_Columns, cols);
mydatasete-> putAndInsertUint16 (DCM_BitsAllocated, bitsAlloc);
mydatasete-> putAndInsertUint16 (DCM_BitsStored, bitsStored);
mydatasete-> putAndInsertUint16 (DCM_HighBit, highBit);
mydatasete-> putAndInsertOFStringArray (DCM_PhotometricInterpretation, photoMetrInt);
mydatasete-> insert (seq, OFFalse, OFFalse);
status = fileformat.saveFile ("c: \\ MultiJpeg2Multi-frameDCMtest-error.dcm", ts);
if (status.bad ())
{
std :: cout << "Error :(" << status.text () << ") \ n";
}
return 0;
}
PS: The DicomUtils class is a static class for DICOM file operations. For details, see the source code of subsequent projects.
The above code can successfully generate a Multi-frame DCM file, and the results should be normal in terms of file size. But when opened, it prompts "Memory cannot be read error", such as:
But the strange thing is that using the dcmdump.exe tool and the preview window (Enable Icons) of Sante DICOM Editor, you can see the normal results. As shown:
Error analysis: JPEG compression in the DICOM standard
The common JPEG compression format in the protocol is given in Appendix A of Part 5 of the DICOM3.0 standard, such as:
The common JPEG image uses 1.2.840.10008.1.2.4.50, and the four test images given in this blog post are in this format. As for the specific compression and encoding process of JPEG, please refer to wikipedia http://zh.wikipedia.org/zh-cn/JPEG.
The standard states that if the DICOM file is a Multi-frame type, each image (frame) needs to be separately compressed (encoded seperately). Compressed data may be fragmented when it is written to the DcmPixelData field in DICOM. Remember: the data in each fragment must come from the same file (ie frame), and each image (frame) is not necessarily stored In the same fragment (fragment), the corresponding relationship between frame and fragment is "one-to-many".
DcmPixelData field (7FE0,0010):
Chapter 8 of Part 5 of the DICOM3.0 standard states that if data is stored in compressed form, then PixelData's VR can only use OB (original data storage is usually OW, if the number of data storage bits is less than or equal to 8, it can also be in OB format, as My last blog post. Compressed data will be divided into multiple fragments containing its own length, and will end with a cutoff character (FFFE, E0DD). As shown:
An image (frame) can be contained in a fragment (fragment), can also be divided into multiple fragments. It can be judged by comparing [Field NumberOfFrames (0028,0008)] with [Field PixelData Item Number-1]
NumberOfFrames == ItemsOfPixelData-1, indicating that each image is contained in a fragment (fragment);
NumberOfFrames <ItemsOfPixelData-1, indicating that an image is stored in multiple fragments;
solution:
Note that the number of items in the PixelData field needs to be [minus 1] above, as shown in Tables A.4-1 and A.4-2. In any case, the PixelData field will contain an Offset item. ——This is exactly the reason why the above code is wrong. To prove this, let us add an empty DcmPixelItem before inserting each image, that is, add the following two lines of code before the for loop:
/ *! ------ zssure: begin, add an empty Dicom Pixel Item as Offset Fragment ------! * /
seq-> insert (new DcmPixelItem (DcmTag (DCM_Item, EVR_OB)));
/ * ------- zssure: end, can successfully solve the problem of storing multiple JPEGs in DCM ------------------------- * /
At this moment, using the DICOM browser, we can successfully open the MultiJPEG2DCMtest.dcm we generated, as shown:
Using the binary viewer, you can see that the PixelData field has an additional SQ Item, which is an empty DcmPixelItem that acts as an offset, as shown:
In addition, it can be seen from the final file size that this method does not reduce storage space.
Remarks:
DICOM3.0 standard provides the compression method and storage method of DCM data. As for whether compressed data (lossy compression, such as 1.2.840.10008.1.2.4.50 used in this example) is clinically applicable, it is not within the scope of the agreement .
PS: I came back to school by accident today and found that the original annual entrance examination was ahead of schedule. I saw everyone waiting hard for the exam outside the exam room in cold weather. I sincerely wish you all to enter your ideal school, ↖ (^ ω ^) ↗.
Follow-up blog post introduction:
fo-dicom builds a simple DICOM Server server
Author: zssure@163.com
Time: 2014-12-27