Add Multi-frame data load support, change some overlay data load logic.

This commit is contained in:
Krad
2022-09-16 15:28:18 +08:00
parent ceca43ceca
commit a4e90dfd1d
8 changed files with 460 additions and 171 deletions

View File

@@ -0,0 +1,38 @@
//
// Created by Krad on 2022/9/16.
//
#include "DICOMFileObjectCache.h"
#include "dcmtk/dcmdata/dcfilefo.h"
void DICOMFileObjectCache::store(const char *filename, DcmFileFormat *file) {
if (!file) return;
if (storage.count(filename) > 0){
delete storage[filename];
}
storage[filename] = file;
}
void DICOMFileObjectCache::remove(const char *filename) {
if (storage.count(filename)>0){
delete storage[filename];
storage.erase(filename);
}
}
void DICOMFileObjectCache::clear() {
std::for_each(storage.begin(),storage.end(), [](auto v){
delete v.second;
});
storage.clear();
}
bool DICOMFileObjectCache::contains(const char *filename) {
return storage.count(filename) > 0;
}
DcmFileFormat *DICOMFileObjectCache::GetFileObject(const char *filename) {
if (!contains(filename)) return nullptr;
return storage[filename];
}

View File

@@ -0,0 +1,29 @@
//
// Created by Krad on 2022/9/16.
//
#ifndef OMEGAV_DICOMFILEOBJECTCACHE_H
#define OMEGAV_DICOMFILEOBJECTCACHE_H
#include <unordered_map>
class DcmFileFormat;
class DICOMFileObjectCache {
public:
static DICOMFileObjectCache* getInstance(){
static DICOMFileObjectCache cache;
return &cache;
}
void store(const char* filename, DcmFileFormat* file);
void remove(const char* filename);
void clear();
bool contains(const char* filename);
DcmFileFormat* GetFileObject(const char* filename);
private:
std::unordered_map<std::string,DcmFileFormat*> storage;
};
#endif //OMEGAV_DICOMFILEOBJECTCACHE_H

View File

@@ -4,6 +4,7 @@
#include "DICOMHeaderHelper.h"
#include "dcmtk/dcmdata/dcfilefo.h"
#include "dcmtk/dcmdata/dcdeftag.h"
#include "dcmtk/dcmdata/dcdatset.h"
#include "dcmtk/dcmdata/dcspchrs.h"
#include "ExtendMedicalImageProperties.h"
@@ -14,42 +15,6 @@
#include <vtkMath.h>
#include <QtCore/qvarlengtharray.h>
struct DICOMTag{
Uint16 group;
Uint16 element;
const char * des;
};
DICOMTag dicom_tags[] = {
{0x0002, 0x0002, "Media storage SOP class uid"},
{0x0002, 0x0003, "Media storage SOP inst uid"},
{0x0002, 0x0010, "Transfer syntax uid"},
{0x0002, 0x0012, "Implementation class uid"},
{0x0008, 0x0018, "Image UID"},
{0x0008, 0x0020, "Series date"},
{0x0008, 0x0030, "Series time"},
{0x0008, 0x0060, "Modality"},
{0x0008, 0x0070, "Manufacturer"},
{0x0008, 0x1060, "Physician"},
{0x0018, 0x0050, "slice thickness"},
{0x0018, 0x0060, "kV"},
{0x0018, 0x0088, "slice spacing"},
{0x0018, 0x1100, "Recon diameter"},
{0x0018, 0x1151, "mA"},
{0x0018, 0x1210, "Recon kernel"},
{0x0020, 0x000d, "Study UID"},
{0x0020, 0x000e, "Series UID"},
{0x0020, 0x0013, "Image number"},
{0x0020, 0x0032, "Patient position"},
{0x0020, 0x0037, "Patient position cosines"},
{0x0020, 0x1041, "Slice location"},
{0x0028, 0x0010, "Num rows"},
{0x0028, 0x0011, "Num cols"},
{0x0028, 0x0030, "pixel spacing"},
{0x0028, 0x0100, "Bits allocated"},
{0x0028, 0x0120, "pixel padding"},
{0x0028, 0x1052, "pixel offset"}
};
void DICOMHeaderHelper::Update() {
ReadHeader();
ArrangeSeriesProperty();
@@ -107,71 +72,177 @@ void DICOMHeaderHelper::readHeaderFromFile(const char * filePath){
// read success!
if (file.loadFile(filePath).good()) {
DcmDataset *dataset = file.getDataset();
DICOMFileHeader header;
header.FilePath = filePath;
std::string SeriesUID;
dataset->findAndGetOFString(DcmTagKey(0x0020, 0x000e), SeriesUID);
long SeriesNumber = 0;
dataset->findAndGetSint32(DcmTagKey(0x0020, 0x0011), header.SeriesNumber);
long AcquisitionNumber = 0;
dataset->findAndGetSint32(DcmTagKey(0x0020, 0x0012), header.AcquisitionNumber);
long InstanceNumber = 0;
dataset->findAndGetSint32(DcmTagKey(0x0020, 0x0013), header.InstanceNumber);
DICOMFileHeader fileHeader;
#define ReadTAGToProperty(Name, group, element)\
std::string Name;\
dataset->findAndGetOFString(DcmTagKey(group, element), Name);
fileHeader.FilePath = filePath;
ReadTAGToProperty(PatientName, 0x0010, 0x0010)
ReadTAGToProperty(StudyUID, 0x0020, 0x000d)
dataset->findAndGetOFString(DCM_SeriesInstanceUID, fileHeader.SeriesUID);
dataset->findAndGetUint16(DcmTagKey(0x0028, 0x0010), header.Rows);
dataset->findAndGetUint16(DcmTagKey(0x0028, 0x0011), header.Columns);
dataset->findAndGetSint32(DCM_SeriesNumber, fileHeader.SeriesNumber);
dataset->findAndGetFloat64(DcmTagKey(0x0028, 0x1050), header.WindowCenter);
dataset->findAndGetFloat64(DcmTagKey(0x0028, 0x1051), header.WindowWidth);
dataset->findAndGetSint32(DCM_AcquisitionNumber, fileHeader.AcquisitionNumber);
dataset->findAndGetSint32(DCM_InstanceNumber, fileHeader.InstanceNumber);
std::string PatientName;
dataset->findAndGetOFString(DCM_PatientName, PatientName);
std::string StudyUID;
dataset->findAndGetOFString(DCM_StudyInstanceUID, StudyUID);
dataset->findAndGetUint16(DCM_Rows, fileHeader.Rows);
dataset->findAndGetUint16(DCM_Columns, fileHeader.Columns);
if (dataset->findAndGetFloat64(DCM_WindowCenter, fileHeader.WindowCenter).bad()){
fileHeader.WindowCenter = 128.0;
}
if (dataset->findAndGetFloat64(DCM_WindowWidth, fileHeader.WindowWidth).bad()){
fileHeader.WindowWidth = 256.0;
}
dataset->findAndGetUint16(DCM_SamplesPerPixel, fileHeader.SamplePerPixel);
//try get pixel spacing
auto spacingResult =
dataset->findAndGetFloat64(DcmTagKey(0x0028, 0x0030), header.Spacing[0], 0);
dataset->findAndGetFloat64(DcmTagKey(0x0028, 0x0030), header.Spacing[1], 1);
dataset->findAndGetFloat64(DCM_PixelSpacing, fileHeader.Spacing[0], 0);
dataset->findAndGetFloat64(DCM_PixelSpacing, fileHeader.Spacing[1], 1);
//No Pixel spacing , try use Imager Pixel Spacing to instead
if (!spacingResult.good()){
spacingResult =
dataset->findAndGetFloat64(DcmTagKey(0x0018, 0x1164), header.Spacing[0], 0);
dataset->findAndGetFloat64(DcmTagKey(0x0018, 0x1164), header.Spacing[1], 1);
dataset->findAndGetFloat64(DCM_ImagerPixelSpacing, fileHeader.Spacing[0], 0);
dataset->findAndGetFloat64(DCM_ImagerPixelSpacing, fileHeader.Spacing[1], 1);
}
//No Imager Pixel Spacing, use default 1.0
if (!spacingResult.good()){
header.Spacing[0] = 1.0;
header.Spacing[1] = 1.0;
fileHeader.Spacing[0] = 1.0;
fileHeader.Spacing[1] = 1.0;
}
dataset->findAndGetFloat64(DcmTagKey(0x0020, 0x0037), header.Orientation[0], 0);
dataset->findAndGetFloat64(DcmTagKey(0x0020, 0x0037), header.Orientation[1], 1);
dataset->findAndGetFloat64(DcmTagKey(0x0020, 0x0037), header.Orientation[2], 2);
dataset->findAndGetFloat64(DcmTagKey(0x0020, 0x0037), header.Orientation[3], 3);
dataset->findAndGetFloat64(DcmTagKey(0x0020, 0x0037), header.Orientation[4], 4);
dataset->findAndGetFloat64(DcmTagKey(0x0020, 0x0037), header.Orientation[5], 5);
dataset->findAndGetFloat64(DcmTagKey(0x0020, 0x0032), header.Position[0], 0);
dataset->findAndGetFloat64(DcmTagKey(0x0020, 0x0032), header.Position[1], 1);
dataset->findAndGetFloat64(DcmTagKey(0x0020, 0x0032), header.Position[2], 2);
dataset->findAndGetUint16(DcmTagKey(0x0028, 0x0002), header.SamplePerPixel);
//thickness
if (dataset->findAndGetFloat64(DCM_SliceThickness,fileHeader.Spacing[2]).bad()){
if (dataset->findAndGetFloat64(DCM_SpacingBetweenSlices,fileHeader.Spacing[2]).bad()){
fileHeader.Spacing[2] = 1.0;
}
}
std::string uniqueID;
uniqueID.append(PatientName);
uniqueID.append("_");
uniqueID.append(StudyUID);
uniqueID.append("_");
uniqueID.append(SeriesUID);
header.SeriesUID = SeriesUID;
uniqueID.append(fileHeader.SeriesUID);
// series be firstly loaded
if (series.count(uniqueID)==0) {
series.emplace(uniqueID,DICOMFileList());
}
header.calculateImagePosition();
series[uniqueID].push_back(std::move(header));
//try to read Orientation and Position
dataset->findAndGetFloat64(DcmTagKey(0x0020, 0x0037), fileHeader.Orientation[0], 0);
dataset->findAndGetFloat64(DcmTagKey(0x0020, 0x0037), fileHeader.Orientation[1], 1);
dataset->findAndGetFloat64(DcmTagKey(0x0020, 0x0037), fileHeader.Orientation[2], 2);
dataset->findAndGetFloat64(DcmTagKey(0x0020, 0x0037), fileHeader.Orientation[3], 3);
dataset->findAndGetFloat64(DcmTagKey(0x0020, 0x0037), fileHeader.Orientation[4], 4);
dataset->findAndGetFloat64(DcmTagKey(0x0020, 0x0037), fileHeader.Orientation[5], 5);
dataset->findAndGetFloat64(DcmTagKey(0x0020, 0x0032), fileHeader.Position[0], 0);
dataset->findAndGetFloat64(DcmTagKey(0x0020, 0x0032), fileHeader.Position[1], 1);
dataset->findAndGetFloat64(DcmTagKey(0x0020, 0x0032), fileHeader.Position[2], 2);
fileHeader.calculateImagePosition();
//multi-frame data
long frameCount = 0;
auto frameRet = dataset->findAndGetSint32(DCM_NumberOfFrames, frameCount);
if (frameRet.good()) {
DcmItem* sharedGroup = nullptr;
if (dataset->findAndGetSequenceItem(DCM_SharedFunctionalGroupsSequence, sharedGroup, 0).good()) {
DcmItem* pixelMeasure = nullptr;
if (sharedGroup->findAndGetSequenceItem(DCM_PixelMeasuresSequence,pixelMeasure, 0).good()){
// use frame pixel spacing
double spacing0 = 0.0;
if(pixelMeasure->findAndGetFloat64(DCM_PixelSpacing,spacing0,0).good()){
fileHeader.Spacing[0] = spacing0;
pixelMeasure->findAndGetFloat64(DCM_PixelSpacing,fileHeader.Spacing[1],1);
}
// try get frame thickness
double spacing2 = 0.0;
if (pixelMeasure->findAndGetFloat64(DCM_SliceThickness,spacing2).good()){
fileHeader.Spacing[2] = spacing2;
}
if (pixelMeasure->findAndGetFloat64(DCM_SpacingBetweenSlices,spacing2).good()){
fileHeader.Spacing[2] = spacing2;
}
}
// try to get frame window setting
DcmItem* frameLUT = nullptr;
if (sharedGroup->findAndGetSequenceItem(DCM_FrameVOILUTSequence,frameLUT, 0).good()){
double wl = 0.0;
if (frameLUT->findAndGetFloat64(DCM_WindowCenter,wl).good()){
fileHeader.WindowCenter = wl;
frameLUT->findAndGetFloat64(DCM_WindowWidth,fileHeader.WindowWidth);
}
}
}
for (int i = 0; i < frameCount; ++i) {
DICOMFileHeader frameHeader(fileHeader);
frameHeader.FrameIndex = i;
DcmItem *perFrameGroup = nullptr;
if (dataset->findAndGetSequenceItem(DCM_PerFrameFunctionalGroupsSequence, perFrameGroup, i).good()) {
//calc instance number for frame
DcmItem *frameContent = nullptr;
if (perFrameGroup->findAndGetSequenceItem(DCM_FrameContentSequence, frameContent, 0).good()) {
unsigned long frameNumber = 0;
frameContent->findAndGetUint32(DCM_InStackPositionNumber, frameNumber);
frameHeader.InstanceNumber = frameNumber + fileHeader.InstanceNumber;
}
// try to get frame position
DcmItem *framePosition = nullptr;
if (perFrameGroup->findAndGetSequenceItem(DCM_PlanePositionSequence, framePosition, 0).good()) {
if(framePosition->findAndGetFloat64(DCM_ImagePositionPatient, frameHeader.Position[0], 0).good()){
framePosition->findAndGetFloat64(DCM_ImagePositionPatient, frameHeader.Position[1], 1);
framePosition->findAndGetFloat64(DCM_ImagePositionPatient, frameHeader.Position[2], 2);
}else{
//restore value
frameHeader.Position[0] = fileHeader.Position[0];
}
}
// try to get frame orientation
DcmItem *frameOrientation = nullptr;
if (perFrameGroup->findAndGetSequenceItem(DCM_PlaneOrientationSequence, frameOrientation,
0).good()) {
if (frameOrientation->findAndGetFloat64(DCM_ImageOrientationPatient,
frameHeader.Orientation[0], 0).good()) {
frameOrientation->findAndGetFloat64(DCM_ImageOrientationPatient, frameHeader.Orientation[1],
1);
frameOrientation->findAndGetFloat64(DCM_ImageOrientationPatient, frameHeader.Orientation[2],
2);
frameOrientation->findAndGetFloat64(DCM_ImageOrientationPatient, frameHeader.Orientation[3],
3);
frameOrientation->findAndGetFloat64(DCM_ImageOrientationPatient, frameHeader.Orientation[4],
4);
frameOrientation->findAndGetFloat64(DCM_ImageOrientationPatient, frameHeader.Orientation[5],
5);
}else{
//restore value
frameHeader.Orientation[0] = fileHeader.Orientation[0];
}
}
}
else{
frameHeader.InstanceNumber = fileHeader.InstanceNumber + i;
}
frameHeader.calculateImagePosition();
series[uniqueID].push_back(std::move(frameHeader));
}
}
else{
series[uniqueID].push_back(std::move(fileHeader));
}
}
}
@@ -204,12 +275,14 @@ void DICOMHeaderHelper::ReadHeader() {
void DICOMHeaderHelper::ArrangeSeriesProperty() {
for (auto item : series) {
//sort by series, instance, AcquisitionNumber
//sort by series, InstanceNumber, AcquisitionNumber
std::sort(item.second.begin(), item.second.end(), [](auto v1, auto v2) {
return v1.SeriesNumber != v2.SeriesNumber ? (v1.SeriesNumber < v2.SeriesNumber) :
(v1.AcquisitionNumber != v2.AcquisitionNumber ? (v1.AcquisitionNumber < v2.AcquisitionNumber) :
(v1.InstanceNumber < v2.InstanceNumber));
});
// split series into image set
auto currentValue = &(item.second[0]);
std::vector<int> splitIndex;
for (int i = 1; i < item.second.size(); ++i) {
@@ -218,10 +291,12 @@ void DICOMHeaderHelper::ArrangeSeriesProperty() {
splitIndex.push_back(i);
}
}
//sort every image set
// use image set to create image property
int imageSetNumber = 0;
int beginOffset = 0;
for (int j = 0; j < splitIndex.size(); ++j) {
//sort every image set with world coordinate position
std::sort(item.second.begin() + beginOffset, item.second.begin() + splitIndex[j],
[](auto v1, auto v2) {
return v1.image_position < v2.image_position;
@@ -246,90 +321,148 @@ void DICOMHeaderHelper::ArrangeSeriesProperty() {
}
ExtendMedicalImageProperties* DICOMHeaderHelper::createProperty(const std::vector<DICOMFileHeader>& headerList, int splitIndex, int beginOffset) {
auto property = ExtendMedicalImageProperties::New();
std::vector<std::string> files;
for (int i = beginOffset; i < splitIndex; ++i) {
files.push_back(headerList[i].FilePath.c_str());
}
const DICOMFileHeader& header = headerList[beginOffset];
property->SetFileNames(files);
// create ExtendMedicalImageProperties and set values
auto property = ExtendMedicalImageProperties::New();
// get values from header
property->SetSeriesUID(header.SeriesUID.c_str());
property->SetSeriesNumber(header.SeriesNumber);
property->SetAcquisitionNumber(header.AcquisitionNumber);
// image reference
property->SetRows(header.Rows);
property->SetColumns(header.Columns);
property->SetSamplePerPixel(header.SamplePerPixel);
property->SetSeriesNumber(header.SeriesNumber);
// property->SetImageNumber(item.second[beginOffset].InstanceNumber);
property->SetAcquisitionNumber(header.AcquisitionNumber);
// window
property->AddWindowLevelPreset(header.WindowWidth,
header.WindowCenter);
property->SetPosition(header.Position[0],header.Position[1], header.Position[2]);
property->SetDirectionCosine(header.Orientation[0],header.Orientation[1],header.Orientation[2],
header.Orientation[3],header.Orientation[4],header.Orientation[5]);
// coordinate
property->SetPosition((double*)header.Position);
property->SetDirectionCosine((double*)header.Orientation);
property->ComputeTransformMatrix();
// set files for this property (split series to image set)
std::vector<std::pair<std::string,long>> files;
for (int i = beginOffset; i < splitIndex; ++i) {
files.push_back({headerList[i].FilePath.c_str(),headerList[i].FrameIndex});
}
property->SetFileNames(files);
// use file to read some additional information
DcmFileFormat file;
// read success!
if (file.loadFile(header.FilePath).good()) {
DcmDataset *dataset = file.getDataset();
#define ReadTAGToProperty(Name, group, element)\
// try to read patient and study information
#define ReadTAGToProperty(Name)\
std::string Name;\
dataset->findAndGetOFString(DcmTagKey(group, element), Name);\
dataset->findAndGetOFString(DCM_##Name, Name);\
property->Set##Name(Name.c_str());
ReadTAGToProperty(PatientBirthDate)
ReadTAGToProperty(PatientSex)
ReadTAGToProperty(PatientAge)
ReadTAGToProperty(StudyDate)
ReadTAGToProperty(AcquisitionDate)
ReadTAGToProperty(StudyTime)
ReadTAGToProperty(AcquisitionTime)
ReadTAGToProperty(Modality)
ReadTAGToProperty(StudyID)
ReadTAGToProperty(PatientBirthDate, 0x0010, 0x0030)
ReadTAGToProperty(PatientSex, 0x0010, 0x0040)
ReadTAGToProperty(PatientAge, 0x0010, 0x1010)
ReadTAGToProperty(StudyDate, 0x0008, 0x0020)
ReadTAGToProperty(AcquisitionDate, 0x0008, 0x0022)
ReadTAGToProperty(StudyTime, 0x0008, 0x0030)
ReadTAGToProperty(AcquisitionTime, 0x0008, 0x0032)
ReadTAGToProperty(Modality, 0x0008, 0x0060)
std::string StudyUID;
dataset->findAndGetOFString(DCM_StudyInstanceUID, StudyUID);
property->SetStudyUID(StudyUID.c_str());
ReadTAGToProperty(StudyUID, 0x0020, 0x000d)
ReadTAGToProperty(StudyID, 0x0020, 0x0010)
// check has overlay
unsigned short overlayRows = 0;
dataset->findAndGetUint16(DCM_OverlayRows, overlayRows);
if (overlayRows > 0){
property->SetHasOverlay(true);
}
// convert text to utf-8 codec
std::string charSet = "";
auto needConvert = dataset->findAndGetOFString(DcmTagKey(0x0008, 0x0005), charSet);
if (needConvert.good()){
DcmSpecificCharacterSet charSetConvert;
charSetConvert.selectCharacterSet(charSet);
#define ReadTAGToPropertyConvert(Name, group, element)\
#define ReadTAGToPropertyConvert(Name)\
std::string Name;\
std::string Name##Convert;\
dataset->findAndGetOFString(DcmTagKey(group, element), Name);\
dataset->findAndGetOFString(DCM_##Name, Name);\
charSetConvert.convertString(Name,Name##Convert);\
property->Set##Name(Name##Convert.c_str());
ReadTAGToPropertyConvert(PatientID, 0x0010, 0x0020)
ReadTAGToPropertyConvert(PatientName, 0x0010, 0x0010)
ReadTAGToPropertyConvert(InstitutionName, 0x0008, 0x0080)
ReadTAGToPropertyConvert(StudyDescription, 0x0008, 0x1030)
ReadTAGToPropertyConvert(SeriesDescription, 0x0008, 0x103E)
ReadTAGToPropertyConvert(PatientID)
ReadTAGToPropertyConvert(PatientName)
ReadTAGToPropertyConvert(InstitutionName)
ReadTAGToPropertyConvert(StudyDescription)
ReadTAGToPropertyConvert(SeriesDescription)
} else{
ReadTAGToProperty(PatientID, 0x0010, 0x0020)
ReadTAGToProperty(PatientName, 0x0010, 0x0010)
ReadTAGToProperty(InstitutionName, 0x0008, 0x0080)
ReadTAGToProperty(StudyDescription, 0x0008, 0x1030)
ReadTAGToProperty(SeriesDescription, 0x0008, 0x103E)
ReadTAGToProperty(PatientID)
ReadTAGToProperty(PatientName)
ReadTAGToProperty(InstitutionName)
ReadTAGToProperty(StudyDescription)
ReadTAGToProperty(SeriesDescription)
}
double thickness= 1.0;
dataset->findAndGetFloat64(DcmTagKey(0x0008, 0x0050), thickness);
double RescaleSlope= 1.0;
dataset->findAndGetFloat64(DcmTagKey(0x0028, 0x1053), RescaleSlope);
double RescaleOffset= 0.0;
dataset->findAndGetFloat64(DcmTagKey(0x0028, 0x1052), RescaleOffset);
// read spacing for image data
double thickness = header.Spacing[2];
// no frame thickness set, read first file thickness
if((int)thickness == 0){
if (dataset->findAndGetFloat64(DCM_SliceThickness, thickness).bad()){
if(dataset->findAndGetFloat64(DCM_SpacingBetweenSlices, thickness).bad())
{
thickness = 1.0;
}
}
}
property->SetSpacing(header.Spacing[0],header.Spacing[1],thickness);
// read data allocate reference param
unsigned short bitsAllocated= 1;
dataset->findAndGetUint16(DcmTagKey(0x0028, 0x0100), bitsAllocated);
unsigned short PixelRepresentation= 0;
dataset->findAndGetUint16(DcmTagKey(0x0028, 0x0103), PixelRepresentation);
property->SetSeriesUID(header.SeriesUID.c_str());
property->SetRows(header.Rows);
property->SetColumns(header.Columns);
property->SetSpacing(header.Spacing[0],header.Spacing[1],thickness);
property->SetRescaleSlope(RescaleSlope);
property->SetRescaleOffset(RescaleOffset);
property->SetBitsAllocated(bitsAllocated);
property->SetPixelRepresentation(PixelRepresentation);
property->SetDirectionCosine((double*)header.Orientation);
property->ComputeTransformMatrix();
// read pixel transform(rescale) param
double RescaleSlope = 1.0;
double RescaleOffset = 0.0;
//try get Rescale params for file first
if (dataset->findAndGetFloat64(DCM_RescaleSlope, RescaleSlope).bad()){
RescaleSlope = 1.0;
}
dataset->findAndGetFloat64(DCM_RescaleIntercept, RescaleOffset);
//try get frame Rescale params
long frameCount = 0;
auto frameRet = dataset->findAndGetSint32(DCM_NumberOfFrames, frameCount);
if (frameRet.good()) {
DcmItem *sharedGroup = nullptr;
if (dataset->findAndGetSequenceItem(DCM_SharedFunctionalGroupsSequence, sharedGroup, 0).good()) {
DcmItem *frameTransform = nullptr;
if (sharedGroup->findAndGetSequenceItem(DCM_PixelValueTransformationSequence,frameTransform,0).good()){
double tempRescaleSlope = 1.0;
if (frameTransform->findAndGetFloat64(DCM_RescaleSlope,tempRescaleSlope).good()){
RescaleSlope = tempRescaleSlope;
frameTransform->findAndGetFloat64(DCM_RescaleIntercept, RescaleOffset);
}
}
}
}
property->SetRescaleSlope(RescaleSlope);
property->SetRescaleOffset(RescaleOffset);
}
else{
printf( " file:%s load error!\r\b", header.FilePath.c_str() );
@@ -337,9 +470,12 @@ ExtendMedicalImageProperties* DICOMHeaderHelper::createProperty(const std::vecto
return nullptr;
}
if (splitIndex-beginOffset>1){
// try to use real world slice origin point distance to instead thickness
const DICOMFileHeader& header2 = headerList[beginOffset+1];
double spacing2 = sqrt(vtkMath::Distance2BetweenPoints(header.Position,header2.Position));
property->SetSpacing(property->GetSpacing()[0],property->GetSpacing()[1],spacing2);
if((int)spacing2 != 0){
property->SetSpacing(property->GetSpacing()[0],property->GetSpacing()[1],spacing2);
}
}
return property;
}

View File

@@ -18,7 +18,9 @@ struct DICOMFileHeader {
long SeriesNumber = 0;
long AcquisitionNumber = 0;
long InstanceNumber = 0;
double Spacing[2] = {1., 1.};
long FrameNumber = -1l;
long FrameIndex = -1l;
double Spacing[3] = {1., 1., .0};
unsigned short Rows;
unsigned short Columns;
unsigned short SamplePerPixel;
@@ -27,6 +29,25 @@ struct DICOMFileHeader {
double Position[3] = {.0, .0, .0};
double Orientation[6] = {.0, .0, .0, .0, .0, .0};
double image_position = 0.0;
DICOMFileHeader() = default;
DICOMFileHeader(const DICOMFileHeader & other){
FilePath = other.FilePath;
SeriesUID = other.SeriesUID;
SeriesNumber = other.SeriesNumber;
AcquisitionNumber = other.AcquisitionNumber;
InstanceNumber = other.InstanceNumber;
FrameNumber = other.FrameNumber;
FrameIndex = other.FrameIndex;
Rows = other.Rows;
Columns = other.Columns;
SamplePerPixel = other.SamplePerPixel;
WindowCenter = other.WindowCenter;
WindowWidth = other.WindowWidth;
image_position = other.image_position;
memcpy(Spacing,other.Spacing, 2* sizeof(double));
memcpy(Position,other.Position, 3* sizeof(double));
memcpy(Orientation,other.Orientation, 6* sizeof(double));
}
bool equalImageSet(const DICOMFileHeader &v) {
return Rows == v.Rows && Columns == v.Columns &&

View File

@@ -14,6 +14,8 @@
#include "openjpeg-2.3/openjpeg.h"
#endif
#include "DICOMFileObjectCache.h"
void DICOMPixelDataHelper::InitCodecs() {
DcmRLEDecoderRegistration::registerCodecs(OFFalse, OFTrue);//注册解码器
/// register JPEG decompression codecs
@@ -42,7 +44,7 @@ void DICOMPixelDataHelper::GetThumbnailData(ExtendMedicalImageProperties *proper
DcmFileFormat *fileFormat = new DcmFileFormat();//读取文件获取传输语法
if (fileFormat->loadFile(property->GetThumbnailFileName()).good()) {
DcmDataset *dset = fileFormat->getDataset();
DicomImage dcmImage(fileFormat, dset->getOriginalXfer(), CIF_MayDetachPixelData);
DicomImage dcmImage(fileFormat, dset->getOriginalXfer());
DicomImage *sdcmImage = dcmImage.createScaledImage(100.0, 0.0, 0, 1);
sdcmImage->setWindow(wl, ww);
sdcmImage->showAllOverlays();
@@ -50,6 +52,7 @@ void DICOMPixelDataHelper::GetThumbnailData(ExtendMedicalImageProperties *proper
unsigned char *outputData = (unsigned char *) sdcmImage->getOutputData(8);//按8位的位宽取数据
data = new unsigned char[length];
memcpy(data, outputData, length);
sdcmImage->deleteOutputData();
delete sdcmImage;
}
delete fileFormat;
@@ -155,50 +158,45 @@ void DICOMPixelDataHelper::GetRGBPixelData(const char *path, void *data, unsigne
}
int DICOMPixelDataHelper::GetOverLayCount(const char *path) {
int ret = 0;
DcmFileFormat fileFormat;
if (fileFormat.loadFile(path).good()) {
DcmDataset * dset = fileFormat.getDataset();
DicomImage dcmImage(&fileFormat, dset->getOriginalXfer(), CIF_MayDetachPixelData);
ret = dcmImage.getOverlayCount();
}
int ret =0;
DcmFileFormat fileFormat;
if (fileFormat.loadFile(path).good()) {
DcmDataset * dset = fileFormat.getDataset();
DicomImage dcmImage(&fileFormat, dset->getOriginalXfer(), CIF_UsePartialAccessToPixelData);
ret = dcmImage.getOverlayCount();
}
return ret;
}
void DICOMPixelDataHelper::GetOverlayData(const char *path, void *data,
unsigned int& width, unsigned int& height, unsigned int plane) {
unsigned int& width, unsigned int& height) {
DcmFileFormat *fileFormat = new DcmFileFormat();//读取文件获取传输语法
if (fileFormat->loadFile(path).good()) {
DcmDataset *dset = fileFormat->getDataset();
DicomImage dcmImage(fileFormat, dset->getOriginalXfer(), CIF_MayDetachPixelData);
if(dcmImage.getOverlayCount()>plane){
const void * overlay = dcmImage.getFullOverlayData(plane,width, height);
int dataLength = width*height;
if (plane == 0){
for (int j = 0; j < dcmImage.getOverlayCount(); ++j) {
const void *overlay = dcmImage.getFullOverlayData(j, width, height);
int dataLength = width * height;
if (j == 0) {
memcpy(data, overlay, dataLength);
}
else{
} else {
int count32 = dataLength / 4;
int tailCount8 = dataLength % 4;
//OR operation header and content
Uint32 * mergeDst = (Uint32*)data;
Uint32 * mergeSrc = (Uint32*)overlay;
Uint32 *mergeDst = (Uint32 *) data;
Uint32 *mergeSrc = (Uint32 *) overlay;
for (int i = 0; i < count32; ++i) {
mergeDst[i] = mergeDst[i] | mergeSrc[i];
}
//OR operation tail
Uint8 * mergeDst8 = (Uint8*)data;
Uint8 * mergeSrc8 = (Uint8*)overlay;
Uint8 *mergeDst8 = (Uint8 *) data;
Uint8 *mergeSrc8 = (Uint8 *) overlay;
for (int i = 1; i <= tailCount8; ++i) {
mergeDst8[dataLength - i] = mergeDst8[dataLength - i] | mergeSrc8[dataLength - i];
}
}
dcmImage.deleteOverlayData();
}
else{
width=-1;
height=-1;
}
}
else{
width=-1;
@@ -206,3 +204,53 @@ void DICOMPixelDataHelper::GetOverlayData(const char *path, void *data,
}
delete fileFormat;
}
void DICOMPixelDataHelper::GetFramePixelData(const char *path, void *data, unsigned long &length, const long& frameIndex) {
auto fileFormat = DICOMFileObjectCache::getInstance()->GetFileObject(path);
if (fileFormat) {
DicomImage dcmImage(fileFormat, fileFormat->getDataset()->getOriginalXfer(), CIF_UsePartialAccessToPixelData,
frameIndex, 1);
if (dcmImage.getStatus() == EIS_Normal) {
const DiPixel *pixelData = dcmImage.getInterData();//rescaled
unsigned long dataBits = 1;
switch (pixelData->getRepresentation()) {
case EPR_Uint8:
case EPR_Sint8:
break;
case EPR_Uint16:
case EPR_Sint16:
dataBits = 2;
break;
case EPR_Uint32:
case EPR_Sint32:
dataBits = 4;
break;
}
const void *dd = pixelData->getData();
length = pixelData->getCount();
length = length * dataBits;
memcpy(data, dd, length);
} else {
length = -1;
}
}
}
bool DICOMPixelDataHelper::StoreFileObject(const char *path) {
DcmFileFormat *fileFormat = new DcmFileFormat();//读取文件获取传输语法
if (fileFormat->loadFile(path).good() && fileFormat->loadAllDataIntoMemory().good()) {
DICOMFileObjectCache::getInstance()->store(path,fileFormat);
return true;
} else{
delete fileFormat;
return false;
}
}
void DICOMPixelDataHelper::ClearFileObject() {
DICOMFileObjectCache::getInstance()->clear();
}
bool DICOMPixelDataHelper::CheckFileObjectCache(const char *path) {
return DICOMFileObjectCache::getInstance()->contains(path);
}

View File

@@ -26,6 +26,14 @@ public:
*/
static void GetPixelData(const char * path, void* data, unsigned long& length);
static bool CheckFileObjectCache(const char * path);
static bool StoreFileObject(const char * path);
static void ClearFileObject();
static void GetFramePixelData(const char * path, void* data, unsigned long& length, const long& frameIndex);
/**
* Get Pixel Data with Rescaled, result is calculate as Float, and arrange as Float.
* @param path dcm file absolute path
@@ -45,7 +53,7 @@ public:
static int GetOverLayCount(const char *path);
static void GetOverlayData(const char * path, void* data,
unsigned int& width, unsigned int& height,unsigned int plane = 0);
unsigned int& width, unsigned int& height);
/**
* Init decompress Codecs, must be called before other method.

View File

@@ -65,11 +65,11 @@ public:
vtkGetVector3Macro(ZVector,double);
const std::vector<std::string>& GetFileNames(){
const std::vector<std::pair<std::string,long>>& GetFileNames(){
return FileNames;
}
void SetFileNames(std::vector<std::string>& files){
void SetFileNames(std::vector<std::pair<std::string,long>>& files){
FileNames = std::move(files);
}
@@ -88,7 +88,7 @@ public:
const char* GetThumbnailFileName(){
if (FileNames.empty()) return nullptr;
return FileNames[0].data();
return FileNames[0].first.data();
}
unsigned long long GetSliceCount(){
return FileNames.size();
@@ -135,7 +135,7 @@ protected:
unsigned short BitsAllocated;
unsigned short PixelRepresentation;
bool HasOverlay = false;
std::vector<std::string> FileNames;
std::vector<std::pair<std::string,long>> FileNames;
std::string uniqueID;
vtkNew<vtkMatrix4x4> OrientationMatrix;
vtkNew<vtkMatrix4x4> WorldToModelMatrix;
@@ -143,8 +143,6 @@ protected:
private:
ExtendMedicalImageProperties(const ExtendMedicalImageProperties&) = delete;
void operator=(const ExtendMedicalImageProperties&) = delete;
};

View File

@@ -98,9 +98,9 @@ void vtkDICOMImageReader2::ExecuteDataWithInformation(vtkDataObject *output,
DICOMPixelDataHelper::InitCodecs();
for (int i = 0; i < properties->GetFileNames().size(); ++i) {
const std::string &path = properties->GetFileNames()[i];
int overlayCount = DICOMPixelDataHelper::GetOverLayCount(path.c_str());
if (overlayCount > 0){
const std::string &path = properties->GetFileNames()[i].first;
const long &frameIndex = properties->GetFileNames()[i].second;
if (properties->GetHasOverlay()){
if (!OverLayData){
HasOverlay = true;
OverLayData = vtkImageData::New();
@@ -112,9 +112,8 @@ void vtkDICOMImageReader2::ExecuteDataWithInformation(vtkDataObject *output,
unsigned int width = 0;
unsigned int height = 0;
void* overlayBuffer = OverLayData->GetScalarPointer(0, 0, i);
for (int j = 0; j < overlayCount; ++j) {
DICOMPixelDataHelper::GetOverlayData(path.c_str(), overlayBuffer, width, height);
}
DICOMPixelDataHelper::GetOverlayData(path.c_str(), overlayBuffer, width, height);
}
@@ -129,14 +128,26 @@ void vtkDICOMImageReader2::ExecuteDataWithInformation(vtkDataObject *output,
DICOMPixelDataHelper::GetRGBPixelData(path.c_str(), buffer, imageDataLength);
} else {
DICOMPixelDataHelper::GetPixelData(path.c_str(), buffer, imageDataLength);
if (frameIndex>=0){
if (!DICOMPixelDataHelper::CheckFileObjectCache(path.c_str())){
if(!DICOMPixelDataHelper::StoreFileObject(path.c_str())){
vtkErrorMacro(<< "Fail to read file:"<<path.c_str());
DICOMPixelDataHelper::ClearFileObject();
// finalize codecs
DICOMPixelDataHelper::FinalizeCodecs();
return;
}
}
DICOMPixelDataHelper::GetFramePixelData(path.c_str(), buffer, imageDataLength, frameIndex);
}
else{
DICOMPixelDataHelper::GetPixelData(path.c_str(), buffer, imageDataLength);
}
}
buffer = ((char *) buffer) + imageDataLength;
}
DICOMPixelDataHelper::ClearFileObject();
// finalize codecs
DICOMPixelDataHelper::FinalizeCodecs();
}