赞
踩
项目中使用了DICOM文件保存图像,之前经常遇到DICOM内放置的是short类型或者float类型的二维图像,按照之前的代码处理JEPG压缩的DICOM文件,当然会出现问题;从网上查到资料,是由于JEPG需要解码器;
把我处理的过程记录下来,以供大家参考;
JPEG的全称是JointPhotographicExpertsGroup(联合图像专家小组),它是一种常用的图像存储格式, jpg/jpeg是24位的图像文件格式,也是一种高效率的压缩格式,文件格式是JPEG(联合图像专家组)标准的产物,该标准由ISO与CCI TT(国际电报电话咨询委员会)共同制定,是面向连续色调静止图像的一种压缩标准。
JPEG格式可以分为标准JPEG、渐进式JPEG和JPEG2000三种格式。
该类型的图片文件,在网络上应用较多,只有图片完全被加载和读取完毕之后,才能看到图片的全貌;它是一种很灵活的图片压缩方式,用户可以在压缩比和图片品质之间进行权衡。不过,通常来讲,其压缩比在10:1到40:1之间,压缩比越大,品质就越差,压缩比越小,品质就越好。JPEG格式压缩的主要是高频信息,对色彩的信息保留较好,适合应用于互联网,可减少图像的传输时间,可以支持24bit真彩色,也普遍应用于需要连续色调的图像。JPEG由于可以提供有损压缩,因此压缩比可以达到其他传统压缩算法无法比拟的程度。其压缩模式有以下几种:顺序式编码(SequentialEncoding),递增式编码(ProgressiveEncoding),无失真编码(LosslessEncoding)和阶梯式编码(HierarchicalEncoding)。
JPEG的压缩,分为四个步骤:
(1)颜色转换:由于JPEG只支持YUV颜色模式,而不支持RGB颜色模式,所以在将彩色图像进行压缩之前,必须先对颜色模式进据转换。转换完成之后还需要进行数据采样。一般采用的采样比例是2:1:1或4:2:2。由于在执行了此项工作之后,每两行数据只保留一行,因此,采样后图像数据量将压缩为原来的一半。
(2)DCT变换:DCT(DiscreteConsineTransform)是将图像信号在频率域上进行变换,分离出高频和低频信息的处理过程。然后再对图像的高频部分(即图像细节)进行压缩,以达到压缩图像数据的目的。首先将图像划分为多个8*8的矩阵。然后对每一个矩阵作DCT变换(变换公式此略)。变换后得到一个频率系数矩阵,其中的频率系数都是浮点数。
(3)量化:由于在后面编码过程中使用的码本都是整数,因此需要对变换后的频率系数进行量化,将之转换为整数。由于进行数据量化后,矩阵中的数据都是近似值,和原始图像数据之间有了差异,这一差异是造成图像压缩后失真的主要原因。
(4)编码:编码采用两种机制:一是0值的行程长度编码;二是熵编码(EntropyCoding)。在JPEG中,采用曲徊序列,即以矩阵对角线的法线方向作“之”字排列矩阵中的元素。这样做的优点是使得靠近矩阵左上角、值比较大的元素排列在行程的前面,而行程的后面所排列的矩阵元素基本上为0值。行程长度编码是非常简单和常用的编码方式,在此不再赘述。编码实际上是一种基于统计特性的编码方法。在JPEG中允许采用HUFFMAN编码或者算术编码。
该类型的图片是对标准JPEG格式的改进,当在网页上下载渐进式JPEG图片时,首先呈现图片的大概外貌,然后再逐渐呈现具体的细节部分,因而被称之为渐进式JPEG。
一种全新的图片压缩发,压缩品质更好,并且改善了无线传输时,因信号不稳定而造成的马赛克及位置错乱等问题。另外,作为JPEG的升级版,JPEG2000的压缩率比标准JPEG高约30%,同时支持有损压缩和无损压缩。它还支持渐进式传输,即,先传输图片的粗略轮廓,然后,逐步传输细节数据,使得图片由模糊到清晰逐步显示。此外,JPEG2000还支持感兴趣区域,也就是说,可以指定图片上感兴趣区域的压缩质量,还可以选择指定的部分先进行解压。
从DICOM文件的0x0002,0x0010的tag可以看到“JPEG lossless”;
dcmjpeg提供了一个压缩/解压缩库以及可用工具。该模块包含一些类,可将DICOM图像对象在非压缩和JPEG压缩表示(传输协议)之间转换。无失真和有失真JPEG处理都被支持。这个模块实现了一族codec(编码解码器,由DcmCodec类派生而来),可以将这些codec在codec list中注册,codec list是由dcmdata模块保存的。
主要接口类:
DJEncoderRegistration: 一个singleton单例类,为所有支持的JPEG处理注册编码器。在djencode.h中定义。
DJDecoderRegistration: 一个singleton单例类,为所有支持的JPEG处理注册解码器。在djdecode.h中定义。
DJCodecEncoder: JPEG编码器的一个抽象codec类,在djcodece.h中定义
DJCodecDecoder: JPEG解码器的一个抽象codec类。
在程序的开始位置,首先注册JPEG的解码器;
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
在程序的结束位置,释放JPEG的解码器;
DJEncoderRegistration::cleanup();
DJDecoderRegistration::cleanup();
DJLSEncoderRegistration::cleanup(); //JPEG-LS encoder cleanup
DJLSDecoderRegistration::cleanup(); //JPEG-LS decoder cleanup
使用到的头文件如下:
#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
#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; void ConvertDigitImageToGrayImageByWindowLevelAndWindowWidth(short* digitImage, short* grayImage, int size, int window_level, int window_width){ double rate = 256.0 / window_width; for (size_t i = 0; i < size; i++){ int tmp = 128.0 + (digitImage[i] - window_level)*rate; if (tmp < 0){ tmp = 0; } else if (tmp > 255){ tmp = 255; } grayImage[i] = tmp; } } 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"; short* pix_buf = new short[512 * 512]; unsigned long size = 0; 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()) { ConvertDigitImageToGrayImageByWindowLevelAndWindowWidth((short*)ptr, pix_buf, size, 1000, 500); } } } Mat img = cv::Mat::zeros(512, 512, CV_8UC3); for (int i = 0; i < size; i++) { uchar* grayrowptr = img.ptr<uchar>(i / 512);//提取行指针 grayrowptr[i % 512 * 3 + 0] = pix_buf[i]; grayrowptr[i % 512 * 3 + 1] = pix_buf[i]; grayrowptr[i % 512 * 3 + 2] = pix_buf[i]; } cv::imwrite("G:\\Data\\gray_9zxr.bmp", img); imshow("Image", img); waitKey(0); system("pause"); }
使用的DICOM文件中图像数据放到了元素组内,需要使用findAndGetElement获取;
DCM文件中数值应该会大于256的灰度值,所以会造成图像的失真,使用窗宽和窗位调整图像显示灰度值,归一化在0-255的范围内,就会显示正常;
代码使用的DICOM文件所在位置为:DICOM文件.rar
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。