DICOM notes - parsing JPEG compressed DCM files

Posted by horsleyaa on Thu, 03 Feb 2022 07:18:20 +0100

DICOM files are used in the project to save images. Before, we often encountered that the two-dimensional images of short type or float type are placed in DICOM. Of course, there will be problems when dealing with the DICOM files compressed by JEPG according to the previous code; The information found on the Internet is because JEPG needs a decoder;
Record my handling process for your reference;

JPEG format

The full name of JPEG is joint photographic experts group. It is a common image storage format. jpg/jpeg is a 24 bit image file format and an efficient compression format. The file format is the product of JPEG standard, which is jointly formulated by ISO and CCITT, It is a compression standard for continuous tone still images.
JPEG format can be divided into standard JPEG, progressive JPEG and JPEG 2000.

Standard JPEG

This type of picture file is widely used on the network. You can see the full picture only after the picture is completely loaded and read; It is a very flexible way of image compression. Users can balance the compression ratio and image quality. However, generally speaking, the compression ratio is between 10:1 and 40:1. The larger the compression ratio, the worse the quality. The smaller the compression ratio, the better the quality. JPEG format compression is mainly high-frequency information, which retains color information well. It is suitable for application in the Internet, can reduce the transmission time of images, can support 24bit true color, and is also widely used in images that need continuous tone. JPEG can provide lossy compression, so the compression ratio can reach a level unmatched by other traditional compression algorithms. Its compression modes are as follows: sequential encoding, progressive encoding, lossless encoding and hierarchical encoding.
JPEG compression is divided into four steps:
(1) color conversion: since JPEG only supports YUV color mode and does not support RGB color mode, the color mode must be converted before compressing the color image. After the conversion, data sampling is also required. Generally, the sampling ratio is 2:1:1 or 4:2:2. Since only one line is reserved for every two lines of data after performing this work, the amount of image data after sampling will be compressed to half of the original.
(2) DCT transform: DCT (discrete construct transform) is a processing process that transforms image signals in the frequency domain and separates high-frequency and low-frequency information. Then the high-frequency part of the image (i.e. image details) is compressed to achieve the purpose of compressing image data. Firstly, the image is divided into multiple 8 * 8 matrices. Then make DCT transformation for each matrix (the transformation formula is omitted). After transformation, a frequency coefficient matrix is obtained, in which the frequency coefficients are floating-point numbers.
(3) quantization: since the codebooks used in the later coding process are integers, it is necessary to quantize the transformed frequency coefficients and convert them into integers. After data quantization, the data in the matrix are approximate values, which is different from the original image data. This difference is the main reason for the distortion after image compression.
(4) coding: two mechanisms are adopted for coding: one is the stroke length coding of 0 value; The second is entropy coding. In JPEG, the zigzag sequence is adopted, that is, the normal direction of the diagonal of the matrix is used as the "zigzag" to arrange the elements in the matrix. The advantage of this is that the elements with large values near the upper left corner of the matrix are arranged in front of the stroke, while the matrix elements arranged behind the stroke are basically 0. Run length coding is a very simple and common coding method, which will not be repeated here. Coding is actually a coding method based on statistical characteristics. HUFFMAN coding or arithmetic coding is allowed in JPEG.

Progressive JPEG

This type of picture is an improvement on the standard JPEG format. When downloading progressive JPEG pictures on the web page, first present the general appearance of the picture, and then gradually present the specific details, so it is called progressive JPEG.

JPEG2002

A brand-new image compression transmission, with better compression quality, and improves the mosaic and position disorder caused by unstable signal during wireless transmission. In addition, as an upgraded version of JPEG, the compression rate of JPEG2000 is about 30% higher than that of standard JPEG, and supports lossy compression and lossless compression at the same time. It also supports progressive transmission, that is, first transmit the rough outline of the picture, and then gradually transmit the detailed data, so that the picture can be displayed gradually from blurred to clear. In addition, JPEG2000 also supports regions of interest, that is, you can specify the compression quality of regions of interest on the picture, and you can also select the specified part to decompress first.

DCMTK parses DCM files in JPEG format

"JPEG lossless" can be seen from the tag of 0x0002 and 0x0010 in DICOM file;

dcmjpeg provides a compression / decompression library and available tools. This module contains classes that convert DICOM image objects between uncompressed and JPEG compressed representations (Transport Protocol). Both undistorted and distorted JPEG processing are supported. This module implements a family of codecs (codecs, derived from the DcmCodec class). These codecs can be registered in the codec list, which is saved by the dcmdata module.
Main interface classes:
DJEncoderRegistration: a singleton singleton class that registers encoders for all supported JPEG processing. In djencode Defined in H.
Djcoderregistration: a singleton singleton class that registers decoders for all supported JPEG processing. In djdecode Defined in H.
Djcodecoder: an abstract codec class of JPEG encoder, which is in djcodece Defined in H
Djcodecoder: an abstract codec class of JPEG decoder.
At the beginning of the program, first register the JPEG decoder;

	DJEncoderRegistration::registerCodecs(
		ECC_lossyYCbCr,
		EUC_default, // UID generation (never create new UID's)
		OFFalse, // verbose
		0, 0, 0, true, ESS_444, true); // optimize huffman table
	DJDecoderRegistration::registerCodecs();
	DJLSEncoderRegistration::registerCodecs();		//JPEG-LS encoder registerCodecs
	DJLSDecoderRegistration::registerCodecs();		//JPEG-LS decoder registerCodecs

Release the JPEG decoder at the end of the program;

DJEncoderRegistration::cleanup();
DJDecoderRegistration::cleanup();
DJLSEncoderRegistration::cleanup();		//JPEG-LS encoder cleanup
DJLSDecoderRegistration::cleanup();		//JPEG-LS decoder cleanup

The header files used are as follows:

#include <dcmtk/dcmjpeg/djdecode.h>  /* for dcmjpeg decoders */
#include <dcmtk/dcmjpeg/djencode.h>
#include <dcmtk/dcmjpls/djdecode.h>		//for JPEG-LS decode
#include <dcmtk/dcmjpls/djencode.h>		//for JPEG-LS encode

code

#include <iostream>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>

#include "dcmtk\config\osconfig.h"
#include "dcmtk\dcmdata\dctk.h"
#include <dcmtk/dcmjpeg/djdecode.h>  /* for dcmjpeg decoders */
#include <dcmtk/dcmjpeg/djencode.h>
#include <dcmtk/dcmjpls/djdecode.h>		//for JPEG-LS decode
#include <dcmtk/dcmjpls/djencode.h>		//for JPEG-LS encode
using namespace std;
using namespace cv;

int main()
{
	DJDecoderRegistration::registerCodecs();

	DJLSEncoderRegistration::registerCodecs();		//JPEG-LS encoder registerCodecs
	DJLSDecoderRegistration::registerCodecs();		//JPEG-LS decoder registerCodecs
	
	DcmFileFormat *myFileFormat = new DcmFileFormat;
	OFCondition cond = myFileFormat->loadFile("G:\\data\\1.DCM");
	if (cond.good()){
		OFString patientName;
		if (myFileFormat->getDataset()->findAndGetOFString(DCM_PatientName, patientName).good())
			std::cout << "Patient Name: " << patientName << "\nTest successed.\n";
		else 
			std::cout << "No Patient Name Data!\n";
	}
	else 
		std::cout << "Error occurs when opening file, check path or filename.\n";
	const Uint16* pix_inbuf = nullptr;
	float* pix_buf = nullptr;
	unsigned long size = 0;
	const char* pbuf = NULL;
	pix_buf = new float[512 * 512];
	
	DcmElement* pElement = nullptr;
	myFileFormat->getDataset()->chooseRepresentation(EXS_DeflatedLittleEndianExplicit, NULL);
	myFileFormat->getDataset()->findAndGetElement(DCM_PixelData, pElement);
	if (pElement != NULL) {
		size = pElement->getLength()/2;
		OFCondition cond = pElement->loadAllDataIntoMemory();
		if (cond.good()) {
			Uint16 * ptr;
			cond = pElement->getUint16Array(ptr);
			if (cond.good()) {
				for (size_t i = 0; i < size; i++){
					pix_buf[i] = ptr[i];
				}				
			}			
		}		
	}
	Mat img = cv::Mat::zeros(512, 512, CV_8UC3);

	for (int i = 0; i < size; i++) {
		uchar* grayrowptr = img.ptr<uchar>(i / 512);//Fetch row pointer
		grayrowptr[i % 512 * 3 + 0] = (int)pix_buf[i];
		grayrowptr[i % 512 * 3 + 1] = (int)pix_buf[i];
		grayrowptr[i % 512 * 3 + 2] = (int)pix_buf[i];
	}
	cv::imwrite("G:\\gray_9zxr.bmp", img);

	imshow("Image", img);
	waitKey(0);
	DJEncoderRegistration::cleanup();
	DJDecoderRegistration::cleanup();
	DJLSEncoderRegistration::cleanup();		//JPEG-LS encoder cleanup
	DJLSDecoderRegistration::cleanup();		//JPEG-LS decoder cleanup
	system("pause");
}

The image data in the DICOM file used is put into the element group and needs to be obtained by findAndGetElement;

The value in the DCM file should be greater than 256 gray value, which will cause image distortion. Adjust the gray value of the image display by using the window width and window level. If it is normalized within the range of 0-255, it will display normally. No processing is done here;

The location of DICOM file used by the code is: DICOM file rar

References

1.Detailed explanation of jpeg image format

Topics: dicom