DICOM medical image processing: DICOM Storage operation-"storing multiple BMP image data into DCM Files", dicomdcm
Background:
The audience of this column "DICOM medical image processing" is narrow. At first, I only wanted to organize my accumulated learning experience and work experience. I browsed it a few days ago and found that the amount of reading is severely polarized, mainly in "conversion of BMP (JPG) and DCM Formats" and "DICOM communication protocol ", in particular, Note 1 of the first blog post DCMTK open source library a long time ago: Save the DCM file as a BMP file or data stream (that is, an array ). Therefore, before the end of 2014, I plan to write a few articles on the format conversion of DCM.How to save BMP, JPG, and other common images as DCM filesBased on the DCMTK library, a simple instance is provided.
These blog posts adoptFlashbackFirst, provide the source code that can be directly run, then focus on the mistakes that are easy to make, and finally add knowledge points.
Use DCMTK to store Multi-BMP into DCM:
The source code is based on DCMTK. For more information, see DCMTK's img2dcm toolkit. The dependent libraries include netapi32.lib, wsock32.lib, and ofstd. lib; oflog. lib; dcmimgle. lib; ijg8.lib; ijg12.lib; ijg16.lib; dcmdata. lib; dcmimage. lib; dcmjpeg. lib; dcmnet. lib; zlib. lib;Libi2d. lib;([Note]: The libi2d. lib library is used to import BMP files)
The source code is as follows:
// 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/libi2d/i2dbmps. h "# include" DicomUtils. h "# include <direct. h> int _ tmain (int argc, _ TCHAR * argv []) {OFCondition status; DcmFileFormat fileformat; DcmDataset * mydatasete = fileformat. getDataset (); Metadata: sums (DcmDataset * &) mydatasete); Uint16 rows, cols, values, bitsAlloc, bitsStored, highBit, pixelRpr, planConf, pixAspectH, pixAspectV; OFString photoMetrInt; Uint32 length; E_TransferSyntax ts; char * mydata = new char [1024*1024*10]; memset (mydata, 0, sizeof (char) * 1024*1024*10); char * tmpData = mydata; char curDir [100]; getcwd (curDir, 100 ); // Add four images cyclically (int I = 0; I <4; ++ I) {OFString num; char numtmp [100]; memset (numtmp, 0, sizeof (char) * 100); sprintf (numtmp, "% s \ test \ % d.bmp", curDir, I + 1); OFString filename = OFString (numtmp ); optional * BMP source = new I2DBmpSource (); BMP source-> setImageFile (filename); char * pixData = NULL; BMP source-> readPixelData (rows, cols, samplePerPixel, photoMetrInt, bitsAlloc, bitsStored, highBit, pixelRpr, planConf, pixAspectH, pixAspectV, pixData, length, ts); memcpy (tmpData, pixData, length); tmpData + = length; delete BMP source ;}; mydatasete-> values (values, samplePerPixel); mydatasete-> putAndInsertString (values, "4"); mydatasete-> values (DCM_Rows, rows); mydatasete-> values (DCM_Columns, cols); mydatasete-> values (values, bitsAlloc); mydatasete-> values (values, bitsStored); mydatasete-> values (DCM_HighBit, highBit); mydatasete-> values (DCM_PixelData, (Uint8 *) mydata, 4 * length); mydatasete-> putAndInsertOFStringArray (bytes, photoMetrInt); // mydatasete-> putAndInsertString (DCM_PlanarConfiguration, "1"); status = fileformat. saveFile ("c: \ multibmp 2dcmtest. dcm ", ts); if (status. bad () {std: cout <"Error :(" <status. text () <") \ n";} return 0 ;}
The DicomUtils class in the Code is a method class that provides a static method AddDicomElement to construct the basic elements of DICOM. The Code is as follows:
# Include "DicomUtils. h" DicomUtils: DicomUtils (void) {} DicomUtils ::~ DicomUtils (void) {} void DicomUtils: AddDicomElements (DcmDataset * & dataset) {// construct test data/* ADD patient information */dataset-> Merge (DCM_AccessionNumber, 0 ); dataset-> putAndInsertString (DCM_PatientName, "zssure", true); dataset-> putAndInsertString (DCM_PatientID, "2234"); dataset-> putAndInsertString (bytes, "20141221 "); dataset-> putAndInsertString (bytes, "M");/* add Study information */dataset-> putAndInsertString (DCM_StudyDate, "20141221"); dataset-> putAndInsertString (DCM_StudyTime, "195411"); char uid [100]; random (uid, serial); dataset-> putAndInsertString (DCM_StudyInstanceUID, uid); dataset-> putAndInsertString (DCM_StudyID, "1111 "); /* add Series information */dataset-> putAndInsertString (DCM_SeriesDate, "20141221"); dataset-> putAndInsertString (DCM_SeriesTime, "195411"); memset (uid, 0, sizeof (char) * 100); random (uid, serial); dataset-> putAndInsertString (DCM_SeriesInstanceUID, uid);/* Add Image information */dataset-> putAndInsertString (DCM_ImageType, "ORIGINAL \ PRIMARY \ AXIAL"); dataset-> putAndInsertString (DCM_ContentDate, "20141221"); dataset-> putAndInsertString (DCM_ContentTime, "200700 "); dataset-> putAndInsertString (DCM_InstanceNumber, "1"); dataset-> putAndInsertString (DCM_SamplesPerPixel, "1"); dataset-> putAndInsertString (bytes, "MONOCHROME2 "); dataset-> putAndInsertString (DCM_PixelSpacing, "0.3 \ 0.3"); dataset-> putAndInsertString (bytes, "16"); dataset-> putAndInsertString (DCM_BitsStored, "16 "); dataset-> putAndInsertString (DCM_HighBit, "15"); dataset-> putAndInsertString (DCM_WindowCenter, "600"); dataset-> putAndInsertString (dcm_1_wwidth, "800 "); dataset-> putAndInsertString (DCM_RescaleIntercept, "0"); dataset-> putAndInsertString (DCM_RescaleSlope, "1 ");}
Problem Analysis: 1) incorrect file format:
The SamplePerPixel label added in DicomUtils is 1 by default. If the final pixel data (called by the readPixelData function) is read, the corresponding SamplePerPixel field of BMP is not re-written, file Format error occurs. The following error is displayed in the SanteSoft DICOM Editor software:
The analysis results of the dw.ump.exe tool using dcmtkare as follows:
It can be seen that the pixel data reading fails.
2) Image Information Display Error:
The correct image is caused by a write error in the Photometric Interpretation field. In the static DicomUtils class, the default Photometric Interpretation value is MONOCHROME2, change to the photoMetrInt parameter returned by the readPixelData function in I2DBmpSource, and the image data is displayed correctly, as shown in:
3) Image Color Display Error:
The Planar Configuration field is not added to the static DicomUtils class. Therefore, DCMTK automatically fills this field with 0. If we change it to 1, an image color error occurs, for example:
BMP format:
There are many blog posts about the BMP format. For details, refer to examples. BMP file data mainly consists of the following parts: 1) file header, that is, structure BITMAPFILEHEADER, * PBITMAPFILEHEADER, similar to dc1_ainfo in DCM; 2) image description information block, this part records the size of the image information block, the image width, height, the number of image channels (that is, Plane), and the number of pixel digits (that is, SamplesPerPixel In the DICOM standard below), image compression mode, image data area size, etc.; 3) color table, that is, the color palette. This part is different from the color palette in DICOM standard. The size of the COLOR table varies with the number of pixels. When the number of pixels is 24 or greater, that is, SamplesPerPixel = 3, the pixel data itself can represent the COLOR, therefore, no color table is required. 4) the image data area is the real pixel information stored in the file. [Note]: Here is a coordinate conversion. The standard pixel storage sequence of BMP files is from left to right, from bottom to top, that is, the coordinate origin is the lower left corner of the image; the DICOM Standard Storage sequence is from left to right, from top to bottom, and the coordinate origin is the upper left corner of the image. Therefore, you need to reverse the sequence when reading the image.
Methods for obtaining BMP image information: 1) directly read binary data
After learning about the specific formats of BMP files, you can use common binary operations to extract pixel data directly from the files. This type of code is also available on the Internet. For more information, see http://www.jb51.net/article/56274.htm.
2) DCMTK Library
The I2DBmpSource class in the DCMTK library is used to parse BMP files and provides conversion from BMP to DICOM data formats. For detailed usage, refer to the above instance or the img2dcm toolkit source code provided by DCMTK.
3) CxImage third-party library
CxImage is a free and excellent image operation class library that allows you to quickly access, display, and convert images, for example, BMP, GIF, ICO, TGA, JPEG, PCX, PNG, TIFF, MNG, and RAS. CxImage is easy to use and has only one API file; supports Windows, Linux, Unix, and other platforms, 32-bit and 64-bit.
For more information about the complex usage of CxImage, see the blog of the Chinese god in CodeProject: http://www.codeproject.com/articles/1300/cximage.
In addition, my learning notes on the DCMTK open source library in blog 1: Save the DCM file as a BMP file or data stream (that is, the source code given in the array) is a combination of the CxImage and DCMTK open source libraries, this is also a common combination method. For details, refer to the source code on my GitHub.
DICOM file format: 1) Samples Per Pixel:
The label is (3rd,). For details, see Appendix C7.6.3.1 of DICOM3.0 standard section. Meaning [the number of separate planes in this image], just like the channel in PhotoShop, each channel represents a color (except for three RGB channels, there will also be the fourth permeability channel ). For gray images (monochrome or gray) and color table images (palette, which are described in the BMP format ),Color paletteThe value of this label is 1. The value of this label is 3 for an RGB image or another color-mode image. The BMP image used in this example is in RGB format, so SamplePerPixel = 3. The initial file format error is caused by setting this field to 1.
2) Photometric Interpretation:
The label is (3rd). For details, refer to Appendix C7.6.3.1.2 in part 1 of DICOM3.0 standard. The common values of this field include MONOCHROME1, MONOCHROME2, palette color, and RGB. MONOCHROME1 and MONOCHROME2 represent single-channel gray images, but the ing between the two is opposite to that of black and white; palette color is the color palette image mentioned in BMP. In this case, the SamplesPerPixel field is 1. RGB is a common three-channel COLOR image, which includes R (red), G (green), and B (blue, the value of SamplesPerPixel is 3, which is the image used in our instance. In addition, the DICOM3.0 standard also provides YBR_FULL, HSV, ARGB, CMYK, and other methods, which are not described in detail here.
3) Planar Configuration:
The label is (3rd). For details, refer to section appendix C7.6.3.1.3 of DICOM3.0 standard. When the value of the Samples Per Pixel field is greater than 1, the Planar Configuration field specifies the storage method of the actual Pixel information, as shown below:
Final result:
In the blog, the sample code is used to generate the multibmp 2dcmtest. dcm file in the root directory of the C drive. You can use the Sante DICOM Editor to open the file and see that the file contains the four bmp images we inserted, for example:
So far, the task of writing multiple BMP images into the DCM file has been successfully completed. In fact, writing multiple images into the DCM file is exactly the same as writing a single BMP image, you only need to write multiple BMP images (in this case, the width and height of each BMP image are the same) to the PixelData label in the DCM at the beginning and end of the pixel data, that is, (7FE0, 0010 ); at this time, the NumberofFrames label is assigned to the image size, and the DCM file editor can automatically identify and extract each image.
Source code:
Baidu Network Disk: http://pan.baidu.com/s/1dDrhHlR
GitHub: https://github.com/zssure-thu/CSDN/tree/master
Follow-up blog introduction:
Save multiple JPEG image data to the DCM File
Building a simple PACS Server using fo-dicom
Author:Zssure@163.com
Time: