由于mitk.net被人恶意抢注, 中科院分子影像重点实验室的www.mitk.net 改到 www.mitk.net.cn 。
目录:
第一章 《DCMTK(MD版)、QT、VS2015编写Dicom序列浏览应用程序-新建项目,配置环境》
第二章 《第二章 基于QT和DCMTK的Dicom 图像浏览器---界面设计》
第三章 《 基于QT和DCMTK的Dicom 图像浏览器---单个Dicom图像读取类》
第四章 《基于QT和DCMTK的Dicom 图像浏览器---检查文件夹下Dicom序列个数》
第五章 《基于QT和DCMTK的Dicom 图像浏览器---Dicom图像序列类》
第六章 《基于QT和DCMTK的Dicom 图像浏览器---Dicom视图类》
第七章 《基于QT和DCMTK的Dicom 图像浏览器---收尾》
一、Dicom图像简单分类
灰度(一般为16位灰度):有压缩 (一般大小为512k), 无压缩(一般大小在100k~300k)
彩色(RGB):有压缩 (JPEG), 无压缩(BPM、PNG)
二、读取Dicom头信息
使用DCMTK的两个类 DcmFileFormat 和 DcmDataset
OFCondition 作为返回来判断执行信息和记录执行日志。
dcmff = new DcmFileFormat;
OFCondition oc = dcmff->loadFile(OFFilename(file.toLocal8Bit()));
if (oc.bad())
{
qDebug() << "Fail:" << oc.text();
return;
}
DcmDataset *dataset = 0;
if (!(dataset = dcmff->getDataset()))
return;
/********读取参数**********/
OFCondition result;
const char *value = NULL;
// 病人信息
result = dataset->findAndGetString(DCM_PatientID, value); // 病历号
QString patientID = QString::fromLocal8Bit(value);
//病人名字
result = dataset->findAndGetString(DCM_PatientName, value);
patientName = QString::fromLocal8Bit(value);
result = dataset->findAndGetString(DCM_PatientBirthDate, value);
patientBirth = QDate::fromString(QString::fromLocal8Bit(value), "yyyyMMdd");
result = dataset->findAndGetString(DCM_PatientSex, value);// 性别
patientSex = QString::fromLocal8Bit(value);
result = dataset->findAndGetString(DCM_PatientAge, value);// 年龄
patientAge = QString::fromLocal8Bit(value);
// study information
result = dataset->findAndGetString(DCM_StudyInstanceUID, value);
studyUid = QString::fromLatin1(value);
result = dataset->findAndGetString(DCM_StudyDate, value);
studyTime.setDate(QDate::fromString(QString::fromLocal8Bit(value), "yyyyMMdd"));
result = dataset->findAndGetString(DCM_StudyTime, value);
studyTime.setTime(formatDicomTime(QString::fromLatin1(value)));
result = dataset->findAndGetString(DCM_StudyDescription, value);
studyDes = QString::fromLocal8Bit(value);
result = dataset->findAndGetString(DCM_ProtocolName, value);
if (result.bad())
// 没有读取到;
else
procId = QString::fromLocal8Bit(value);// 读取成功
result = dataset->findAndGetString(DCM_BodyPartExamined, value);
bodyPart = QString::fromLocal8Bit(value);
result = dataset->findAndGetString(DCM_PatientPosition, value);
bodyPos = QString::fromLocal8Bit(value);
//series information
result = dataset->findAndGetString(DCM_Manufacturer, value);
manufacturer = QString::fromLocal8Bit(value);
result = dataset->findAndGetString(DCM_Modality, value);
modality = QString::fromLocal8Bit(value);
result = dataset->findAndGetString(DCM_ManufacturerModelName, value);
modelName = QString::fromLocal8Bit(value);
result = dataset->findAndGetString(DCM_AccessionNumber, value);
accessionNumber = QString::fromLatin1(value);
result = dataset->findAndGetString(DCM_SeriesInstanceUID, value);
seriesUid = QString::fromLatin1(value);
result = dataset->findAndGetString(DCM_SeriesNumber, value);
seriesNumber = QString::fromLatin1(value);
result = dataset->findAndGetString(DCM_SeriesDescription, value);
seriesDes = QString::fromLocal8Bit(value);
//instance information
result = dataset->findAndGetString(DCM_SOPInstanceUID, value);
instanceUid = QString::fromLatin1(value);
result = dataset->findAndGetString(DCM_SOPClassUID, value);
sopClassUid = QString::fromLatin1(value);
result = dataset->findAndGetString(DCM_InstanceNumber, value);
instanceNumber = QString(value);
result = dataset->findAndGetString(DCM_AcquisitionDate, value);
if (value == NULL)
result = dataset->findAndGetString(DCM_ContentDate, value);
acquisitionTime.setDate(QDate::fromString(QString::fromLatin1(value), "yyyyMMdd"));
result = dataset->findAndGetString(DCM_AcquisitionTime, value);
if (value == NULL)
result = dataset->findAndGetString(DCM_ContentTime, value);
acquisitionTime.setTime(formatDicomTime(QString::fromLatin1(value)));
result = dataset->findAndGetString(DCM_InstitutionName, value);
institution = QString::fromLocal8Bit(value);
result = dataset->findAndGetString(DCM_PatientPosition, value);
patientPostion = QString::fromLatin1(value);
result = dataset->findAndGetString(DCM_RequestingPhysician, value);
reqPhysician = QString::fromLocal8Bit(value);
result = dataset->findAndGetString(DCM_PerformingPhysicianName, value);
perPhysician = QString::fromLocal8Bit(value);
/*******图像相关信息*******/
float pixelSpacingY,pixelSpacingX,sliceThickness; // 单个像素间距
result = dataset->findAndGetFloat64(DCM_PixelSpacing, pixelSpacingY, 0);
result = dataset->findAndGetFloat64(DCM_PixelSpacing, pixelSpacingX, 1);
result = dataset->findAndGetFloat64(DCM_SliceThickness, sliceThickness);
float startX,startY,sliceLocation; // 实际起始位置
result = dataset->findAndGetFloat64(DCM_ImagePositionPatient, startX, 0);
result = dataset->findAndGetFloat64(DCM_ImagePositionPatient, startY, 1);
result = dataset->findAndGetFloat64(DCM_SliceLocation, sliceLocation);
float intercept;
result = dataset->findAndGetFloat64(DCM_RescaleIntercept, intercept);
// 窗宽和窗位
float winWidth,winCenter;
result = dataset->findAndGetFloat64(DCM_WindowWidth, winWidth);
result = dataset->findAndGetFloat64(DCM_WindowCenter, winCenter);
result = dataset->findAndGetString(DCM_KVP, value);
if (result.bad())
kvp = 0;
else
kvp = QString::fromLatin1(value).toDouble();
result = dataset->findAndGetString(DCM_Exposure, value);
if (result.bad())
mAs = 0;
else
mAs = QString::fromLatin1(value).toDouble();
result = dataset->findAndGetString(DCM_ExposureTime, value);
if (result.bad())
mS = 0;
else
mS = QString::fromLatin1(value).toDouble();
result = dataset->findAndGetString(DCM_XRayTubeCurrent, value);
if (result.bad())
mA = 0;
else
mA = QString::fromLatin1(value).toDouble();
二、读取Dicom图像数据
1) 判断是否有压缩:
bool isYaSuo = false; // 无压缩
std::string losslessTransUID = "1.2.840.10008.1.2.4.70";
std::string lossTransUID = "1.2.840.10008.1.2.4.51";
std::string losslessP14 = "1.2.840.10008.1.2.4.57";
std::string lossyP1 = "1.2.840.10008.1.2.4.50";
std::string lossyRLE = "1.2.840.10008.1.2.5";
E_TransferSyntax xfer = dataset->getOriginalXfer();
const char* transferSyntax = NULL;
dcmff->getMetaInfo()->findAndGetString(DCM_TransferSyntaxUID, transferSyntax);
if (transferSyntax == NULL)
isYaSuo = false; // 无压缩
else {
if (transferSyntax == losslessTransUID || transferSyntax == lossTransUID ||
transferSyntax == losslessP14 || transferSyntax == lossyP1)
{
//对压缩的图像像素进行解压
DJDecoderRegistration::registerCodecs();
dataset->chooseRepresentation(EXS_LittleEndianExplicit, NULL);
DJDecoderRegistration::cleanup();
isYaSuo = true; // 有压缩
}
else if (transferSyntax == lossyRLE)
{
DcmRLEDecoderRegistration::registerCodecs();
dataset->chooseRepresentation(EXS_LittleEndianExplicit, NULL);
DcmRLEDecoderRegistration::cleanup();
isYaSuo = true;// 有压缩
}
else
{
isYaSuo = false; //无压缩
}
}
2) 使用DicomImage 类读取图像像素数据 :
有压缩的情况
DicomImage(DcmObject *object, const E_TransferSyntax xfer, const unsigned long flags = 0,);
参数 object 指向DcmDataset的指针。xfer 为压缩解压。flags设置为CIF_TakeOverExternalDataset,不能析构object指针,因为在析构DicomImage指针时,会自动析构oject, 因此只需delete dcmImage
无压缩的情况
DicomImage(DcmObject *object, const E_TransferSyntax xfer, const unsigned long flags = 0,);
参数 object 指向DcmFileFormat的指针。xfer 为dataset->getOriginalXfer()。flags设置为CIF_TakeOverExternalDataset,不能析构object指针,因为在析构DicomImage指针时,会自动析构oject, 因此只需delete dcmImage
if (isYaSuo)
{
dcmImage = new DicomImage((DcmObject*)dataset, dataset->getOriginalXfer(), CIF_TakeOverExternalDataset);
if (dcmImage->getStatus() == EIS_Normal) {
// 读取正确
}
}
}
else
{
dcmImage = new DicomImage(dcmff, dataset->getOriginalXfer(), CIF_TakeOverExternalDataset);
if (dcmImage->getStatus() == EIS_Normal) {
读取正确
}
}
读取灰度图像灰度值,如果是灰度图像。
使用DiPixel类
DicomImage *image = dcmImage;
// 判断是否时正确DICOM图像
if(!(image->getStatus() == EIS_Normal))
return ;
if(image->isMonochrome())
{
const DiPixel* pixel = image->getInterData();
//读取图像在像素点(x,y) 位置的值 value
qint64 value;
if (pixel && (x < image->getWidth()) && (x >= 0)
&& (y <image->getHeight()) && (y >= 0)) {
EP_Representation r = pixel->getRepresentation(); // 读取数据类型。
switch (r) {
case EPR_Sint8:
value = *((qint8*)(pixel->getData()) + (y * image->getWidth() + x));
break;
case EPR_Uint8:
value = *((quint8*)(pixel->getData()) + (y * image->getWidth() + x));
break;
case EPR_Sint16:
value = *((qint16*)(pixel->getData()) + (y * image->getWidth() + x));
break;
case EPR_Uint16:
value = *((quint16*)(pixel->getData()) + (y * image->getWidth() + x));
break;
case EPR_Sint32:
value = *((qint32*)(pixel->getData()) + (y * image->getWidth() + x));
break;
case EPR_Uint32:
value = *((quint32*)(pixel->getData()) + (y * image->getWidth() + x));
break;
}
}
}
读取RGB图像,如果是RGB图像。
DicomImage *image = dcmImage;
// 判断是否时正确DICOM图像
if(!(image->getStatus() == EIS_Normal))
return ;
if(image->isMonochrome())
{
}
else
{
unsigned char * pixelDate=(unsigned char *) image->getOutputData(8,0,0);
int w = image->getWidth();
int h = image->getHeight();
// 在像素的点(x,y) 下的RGB值
B = *(pixelDate + y*w*3 + x*3 +2);
G = *(pixelDate + y*w*3 + x*3 +1);
R = *(pixelDate + y*w*3 + x*3);
}
三、C++类设计
对于灰度图的伪彩色表类。添加C++类 ColorIndexTable
ColorIndexTable.h 文件
#pragma once
#include<Windows.h>
class ColorIndexTable
{
public:
enum ColorMode
{
gray = 0,
green,
blue,
red,
FireLut
};
ColorIndexTable(ColorMode color);
~ColorIndexTable();
RGBQUAD palette[256];// 调色板RGBQUAD的大小就是256
size_t getPaletteBits();
};
ColorIndexTable.cpp 文件
#include "ColorIndexTable.h"
const int Table_FireLutB[256] = { 0,8,12,16,20,24,28,32,36,40,44,48,52,56,60,64,68,72,76,80,84,88,92,96,100,104,108,112,116,120,124,128,132,136,
140,144,148,152,156,160,164,168,172,176,180,184,188,192,196,200,204,208,212,216,220,217,214,211,208,
205,202,199,196,193,190,187,184,181,178,175,172,169,166,163,160,157,154,151,148,145,142,139,136,133,
130,127,124,121,118,115,112,109,106,103,100,97,94,91,88,85,82,79,76,73,70,67,64,61,58,55,52,49,46,43,40,
37,34,31,28,25,22,19,16,13,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,10,20,30,40,50,60,70,80,90,100,110,120,130,140,150,160,170,180,190,200,200 };
const int Table_FireLutG[256] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,4,6,8,10,12,14,16,18,20,
22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,72,74,76,78,80,82,84,86,88,90,
92,94,96,98,100,102,104,106,108,110,112,114,116,118,120,122,124,126,128,130,132,134,136,138,140,142,
144,146,148,150,152,154,156,158,160,162,164,166,168,170,172,174,176,178,180,182,184,186,188,190,
192,194,196,198,200,202,204,206,208,210,212,214,216,218,220,222,224,226,228,230,232,234,236,238,
240,242,244,246,248,250,252,254,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255 };
const int Table_FireLutR[256] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,6,9,12,15,18,21,
24,27,30,33,36,39,42,45,48,51,54,57,60,63,66,69,72,75,78,81,84,87,90,93,96,99,102,105,108,111,114,117,
120,123,126,129,132,135,138,141,144,147,150,153,156,159,162,165,168,171,174,177,180,183,186,189,
192,195,198,201,204,207,210,213,216,219,222,225,228,231,234,237,240,243,246,249,252,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 };
ColorIndexTable::ColorIndexTable(ColorMode color)
{
memset(palette, 0, sizeof(palette));
switch (color)
{
case gray:
// 灰度
for (int i = 0; i < 256; ++i) {
palette[i].rgbBlue = i;
palette[i].rgbGreen = i;
palette[i].rgbRed = i;
}
break;
case green:
// 绿色
for (int i = 0; i < 256; ++i) {
palette[i].rgbBlue = 0;
palette[i].rgbGreen = i;
palette[i].rgbRed = 0;
}
break;
case blue:
// 蓝色
for (int i = 0; i < 256; ++i) {
palette[i].rgbBlue = i;
palette[i].rgbGreen = 0;
palette[i].rgbRed = 0;
}
break;
case red:
// 红色
for (int i = 0; i < 256; ++i) {
palette[i].rgbBlue = 0;
palette[i].rgbGreen = 0;
palette[i].rgbRed = i;
}
break;
case FireLut:
// 从Table_FireLut表索引
for (int i = 0; i < 256; ++i) {
palette[i].rgbBlue = Table_FireLutB[i];
palette[i].rgbGreen = Table_FireLutG[i];
palette[i].rgbRed = Table_FireLutR[i];
}
break;
default:
break;
}
}
ColorIndexTable::~ColorIndexTable()
{
}
size_t ColorIndexTable::getPaletteBits()
{
return sizeof(palette);
}
添加单个图像读取类
image.h
#pragma once
#include <QString>
#include <QPixmap>
#include "dcmtk/dcmimgle/diutils.h"
class DicomImage;
class DcmFileFormat;
class DiPixel;
class Image
{
public:
explicit Image(const QString &file);
~Image();
// 判断是否读取成功
bool isNormal() const;
// 窗宽窗位
void getWindow(double ¢er, double &width) const
{
center = winCenter; width = winWidth;
}
QString getSeriesUID() const
{
return seriesUID;
}
int getInstanceNumber();
bool getPixSpacing(double & spacingX, double & spacingY, double & spacingZ) const;
bool getImageSize(int &width, int &height) const;
bool getPixmap(QPixmap & pixmap); // 得到该图像的位图 pixmap
QString getPixInfo(int x, int y); // 在(x,y) 处的像素值
bool isMonochrome(); // 是否是灰度图
const DiPixel *getInternalPtr();
static bool DicomImageToPixmap(DicomImage & dcmImage, QPixmap & pixmap);
static bool UcharArrayToPixmap(uchar *data, int w, int h,int bitSize, QPixmap & pixmap,int biBitCount=8);
//set
static void setColor(int &color)
{
Image::color = color;
}
void setWindow(const double ¢er, const double &width)
{
winCenter = center; winWidth = width;
}
void setWindowDelta(const double &dCenter, const double &dWidth)
{
winCenter += dCenter; winWidth += dWidth;
}
void setRoiWindow(const QRectF &rect);
private:
// 图像所属序列的唯一标识
QString seriesUID;
// 图像在序列中的编号(位置)
QString instanceNumber;
// 像素间距,用于显示比例
double spaceX, spaceY, spaceZ;
// 图像宽度、高度
int imageWidth, imageHeight;
// 窗宽、位
double winWidth;
double winCenter;
DcmFileFormat *dcmff;
DicomImage *dcmImage;
// 颜色映射(灰度图像有效)
static int color; // 选择伪彩色
void init(); // 对属性赋值
};
image.cpp
#include "Image.h"
#include "ColorIndexTable.h"
#include "dcmtk/dcmdata/dcfilefo.h"
#include "dcmtk/dcmimgle/dcmimage.h"
#include "dcmtk/dcmdata/dcdeftag.h"
#include "dcmtk/dcmdata/dctk.h"
#include "dcmtk/dcmdata/dcrledrg.h"
#include "dcmtk/dcmimage/diregist.h"
#include "dcmtk/dcmjpeg/djdecode.h"
#include <qdebug.h>
Image::Image(const QString &file):dcmff(new DcmFileFormat), dcmImage(0)
{
OFCondition oc = dcmff->loadFile(OFFilename(file.toLocal8Bit()));
if (oc.bad())
{
qDebug() << "Fail:" << oc.text();
return; // 此时 dcmImage=0; 不执行init()
}
init();
}
Image::~Image()
{
if (dcmImage)
{
delete dcmImage;
dcmImage = 0;
dcmff = 0;
/*创建dcmImage时设置了CIF_TakeOverExternalDataset因此只需delete dcmImage*/
}
else {
delete dcmff; dcmff = 0;
}
}
bool Image::isNormal() const
{
return dcmImage && (dcmImage->getStatus() == EIS_Normal);
}
void Image::init()
{
DcmDataset *dataset = 0;
OFCondition result;
const char *value = NULL;
if (!(dataset = dcmff->getDataset()))
return;
// 头信息读取
result = dataset->findAndGetString(DCM_SeriesInstanceUID, value);
if (result.bad())
return;
seriesUID = QString::fromLatin1(value);
result = dataset->findAndGetString(DCM_InstanceNumber, value);
if (result.bad())
return;
instanceNumber = QString(value);
result = dataset->findAndGetFloat64(DCM_PixelSpacing, spaceY, 0);
if (result.bad())
spaceY = 1;
result = dataset->findAndGetFloat64(DCM_PixelSpacing, spaceX, 1);
if (result.bad())
spaceX = 1;
result = dataset->findAndGetFloat64(DCM_SliceThickness, spaceZ);
if (result.bad())
spaceZ = 1;
result = dataset->findAndGetFloat64(DCM_WindowWidth, winWidth);
result = dataset->findAndGetFloat64(DCM_WindowCenter, winCenter);
// 创建DcmImage
/********解压缩**********/
bool isYaSuo = 0;
std::string losslessTransUID = "1.2.840.10008.1.2.4.70";
std::string lossTransUID = "1.2.840.10008.1.2.4.51";
std::string losslessP14 = "1.2.840.10008.1.2.4.57";
std::string lossyP1 = "1.2.840.10008.1.2.4.50";
std::string lossyRLE = "1.2.840.10008.1.2.5";
E_TransferSyntax xfer = dataset->getOriginalXfer();
const char* transferSyntax = NULL;
dcmff->getMetaInfo()->findAndGetString(DCM_TransferSyntaxUID, transferSyntax);
if (transferSyntax == NULL)
isYaSuo = false;// 无压缩
else {
if (transferSyntax == losslessTransUID || transferSyntax == lossTransUID ||
transferSyntax == losslessP14 || transferSyntax == lossyP1)
{
//对压缩的图像像素进行解压
DJDecoderRegistration::registerCodecs();
dataset->chooseRepresentation(EXS_LittleEndianExplicit, NULL);
DJDecoderRegistration::cleanup();
isYaSuo = true;// 有压缩
}
else if (transferSyntax == lossyRLE)
{
DcmRLEDecoderRegistration::registerCodecs();
dataset->chooseRepresentation(EXS_LittleEndianExplicit, NULL);
DcmRLEDecoderRegistration::cleanup();
isYaSuo = true;// 有压缩
}
else
{
isYaSuo = false;// 无压缩
}
}
if (isYaSuo)
{
dcmImage = new DicomImage((DcmObject*)dataset, dataset->getOriginalXfer(), CIF_TakeOverExternalDataset);
if (dcmImage->getStatus() == EIS_Normal) {
imageWidth = dcmImage->getWidth();
imageHeight = dcmImage->getHeight();
if (winWidth < 1) {
// 设置窗宽窗位
dcmImage->setRoiWindow(0, 0, imageWidth, imageHeight);
// 重新对winCenter, winWidth赋值
dcmImage->getWindow(winCenter, winWidth);
}
}
}
else
{
dcmImage = new DicomImage(dcmff, dataset->getOriginalXfer(), CIF_TakeOverExternalDataset);
if (dcmImage->getStatus() == EIS_Normal) {
imageWidth = dcmImage->getWidth();
imageHeight = dcmImage->getHeight();
if (winWidth < 1) {
dcmImage->setRoiWindow(0, 0, imageWidth, imageHeight);
dcmImage->getWindow(winCenter, winWidth);
}
}
}
}
int Image::getInstanceNumber()
{
return instanceNumber.toInt();
}
bool Image::getPixSpacing(double & spacingX, double & spacingY, double & spacingZ) const
{
if (isNormal()) {
spacingX = spaceX;
spacingY = spaceY;
spacingZ = spaceZ;
return true;
}
return false;
}
bool Image::getImageSize(int & width, int & height) const
{
if(!isNormal())
return false;
width = imageWidth; height = imageHeight;
return true;
}
bool Image::getPixmap(QPixmap & pixmap)
{
if (isNormal()) {
dcmImage->setWindow(winCenter, winWidth);
return DicomImageToPixmap(*dcmImage, pixmap);
}
return false;
}
QString Image::getPixInfo(int x, int y)
{
if (!isNormal())
return"";
if (dcmImage->isMonochrome())
{
const DiPixel* pixel = dcmImage->getInterData();
//读取图像在像素点(x,y) 位置的值 value
qint64 value = 0;
if (pixel && (x < imageWidth) && (x >= 0)
&& (y <imageHeight) && (y >= 0)) {
EP_Representation r = pixel->getRepresentation(); // 读取数据类型。
switch (r) {
case EPR_Sint8:
value = *((qint8*)(pixel->getData()) + (y * imageWidth + x));
break;
case EPR_Uint8:
value = *((quint8*)(pixel->getData()) + (y * imageWidth + x));
break;
case EPR_Sint16:
value = *((qint16*)(pixel->getData()) + (y * imageWidth + x));
break;
case EPR_Uint16:
value = *((quint16*)(pixel->getData()) + (y * imageWidth + x));
break;
case EPR_Sint32:
value = *((qint32*)(pixel->getData()) + (y * imageWidth + x));
break;
case EPR_Uint32:
value = *((quint32*)(pixel->getData()) + (y * imageWidth + x));
break;
}
}
return QStringLiteral("灰度值:%1").arg(value); // 带中文字符是使用QStringLiteral
}else
{
unsigned char * pixelDate = (unsigned char *)dcmImage->getOutputData(8, 0, 0);
// 在像素的点(x,y) 下的RGB值
int B = 0; int G = 0; int R = 0;
if (pixelDate && (x < imageWidth) && (x >= 0)
&& (y < imageHeight) && (y >= 0))
{
B = *(pixelDate + y*imageWidth * 3 + x * 3 + 2);
G = *(pixelDate + y*imageWidth * 3 + x * 3 + 1);
R = *(pixelDate + y*imageWidth * 3 + x * 3);
}
return QStringLiteral("R:%1, G:%2, B:%3").arg(R).arg(G).arg(B); // 带中文字符是使用QStringLiteral
}
}
bool Image::isMonochrome()
{
return dcmImage->isMonochrome();
}
const DiPixel * Image::getInternalPtr()
{
return isNormal() ? dcmImage->getInterData() : nullptr;
}
bool Image::DicomImageToPixmap(DicomImage & dcmImage, QPixmap & pixmap)
{
bool res = true;
void *pDIB = NULL;
int size = 0;
if (dcmImage.isMonochrome())
{
// 灰度图像
size = dcmImage.createWindowsDIB(pDIB, 0, 0, 8, 1, 1);
if (!pDIB)
return false;
res=UcharArrayToPixmap((uchar *)pDIB, dcmImage.getWidth(), dcmImage.getHeight(), size, pixmap);
}
else
{
// RGB图像
size = dcmImage.createWindowsDIB(pDIB, 0, 0, 24, 1, 1);
if (!pDIB)
return false;
res = UcharArrayToPixmap((uchar *)pDIB, dcmImage.getWidth(), dcmImage.getHeight(), size, pixmap,24);
}
delete pDIB;
return res;
}
bool Image::UcharArrayToPixmap(uchar *data, int w, int h, int bitSize, QPixmap & pixmap, int biBitCount = 8)
{
//位图文件由四部分依序组成:BITMAPFILEHEADER,BITMAPINFOHEADER,调色板,Image Data。
BITMAPFILEHEADER lpfh;// 文件头 固定的14个字节, 描述文件的有关信息
BITMAPINFOHEADER lpih;// 固定的40个字节,描述图像的有关信息
ColorIndexTable rgbquad((ColorIndexTable::ColorMode)color);
RGBQUAD *palette = rgbquad.palette;// 调色板RGBQUAD的大小就是256
memset(&lpfh, 0, sizeof(BITMAPFILEHEADER));
lpfh.bfType = 0x4d42;//'B''M' must be 0x4D42.
//the sum bits of BITMAPFILEHEADER,BITMAPINFOHEADER and RGBQUAD;the index byte of the image data.
lpfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + rgbquad.getPaletteBits();
memset(&lpih, 0, sizeof(BITMAPINFOHEADER));
lpih.biSize = sizeof(BITMAPINFOHEADER); //the size of this struct. it is 40 bytes.
lpih.biWidth = w;
lpih.biHeight = h;
lpih.biCompression = BI_RGB;
lpih.biPlanes = 1; //must be 1.
void *pDIB = data;
int size = bitSize;
lpih.biBitCount = biBitCount;
//the size of the whole bitmap file.
lpfh.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + rgbquad.getPaletteBits() + size;
QByteArray bmp;
bmp.append((char*)&lpfh, sizeof(BITMAPFILEHEADER));
bmp.append((char*)&lpih, sizeof(BITMAPINFOHEADER));
bmp.append((char*)palette, rgbquad.getPaletteBits());
bmp.append((char*)pDIB, size);
return pixmap.loadFromData(bmp);
}
void Image::setRoiWindow(const QRectF & rect)
{
if (!isNormal())
return;
dcmImage->setRoiWindow(rect.left(), rect.top(), rect.width(), rect.height());
dcmImage->getWindow(winCenter, winWidth);
}
int Image::color=0;