Add Multi-frame data load support, change some overlay data load logic.
This commit is contained in:
38
src/src/IO/DICOM/DICOMFileObjectCache.cpp
Normal file
38
src/src/IO/DICOM/DICOMFileObjectCache.cpp
Normal 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];
|
||||||
|
}
|
||||||
29
src/src/IO/DICOM/DICOMFileObjectCache.h
Normal file
29
src/src/IO/DICOM/DICOMFileObjectCache.h
Normal 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
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include "DICOMHeaderHelper.h"
|
#include "DICOMHeaderHelper.h"
|
||||||
#include "dcmtk/dcmdata/dcfilefo.h"
|
#include "dcmtk/dcmdata/dcfilefo.h"
|
||||||
|
#include "dcmtk/dcmdata/dcdeftag.h"
|
||||||
#include "dcmtk/dcmdata/dcdatset.h"
|
#include "dcmtk/dcmdata/dcdatset.h"
|
||||||
#include "dcmtk/dcmdata/dcspchrs.h"
|
#include "dcmtk/dcmdata/dcspchrs.h"
|
||||||
#include "ExtendMedicalImageProperties.h"
|
#include "ExtendMedicalImageProperties.h"
|
||||||
@@ -14,42 +15,6 @@
|
|||||||
#include <vtkMath.h>
|
#include <vtkMath.h>
|
||||||
#include <QtCore/qvarlengtharray.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() {
|
void DICOMHeaderHelper::Update() {
|
||||||
ReadHeader();
|
ReadHeader();
|
||||||
ArrangeSeriesProperty();
|
ArrangeSeriesProperty();
|
||||||
@@ -107,71 +72,177 @@ void DICOMHeaderHelper::readHeaderFromFile(const char * filePath){
|
|||||||
// read success!
|
// read success!
|
||||||
if (file.loadFile(filePath).good()) {
|
if (file.loadFile(filePath).good()) {
|
||||||
DcmDataset *dataset = file.getDataset();
|
DcmDataset *dataset = file.getDataset();
|
||||||
DICOMFileHeader header;
|
|
||||||
header.FilePath = filePath;
|
|
||||||
std::string SeriesUID;
|
|
||||||
dataset->findAndGetOFString(DcmTagKey(0x0020, 0x000e), SeriesUID);
|
|
||||||
|
|
||||||
long SeriesNumber = 0;
|
DICOMFileHeader fileHeader;
|
||||||
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);
|
|
||||||
|
|
||||||
#define ReadTAGToProperty(Name, group, element)\
|
fileHeader.FilePath = filePath;
|
||||||
std::string Name;\
|
|
||||||
dataset->findAndGetOFString(DcmTagKey(group, element), Name);
|
|
||||||
|
|
||||||
ReadTAGToProperty(PatientName, 0x0010, 0x0010)
|
dataset->findAndGetOFString(DCM_SeriesInstanceUID, fileHeader.SeriesUID);
|
||||||
ReadTAGToProperty(StudyUID, 0x0020, 0x000d)
|
|
||||||
|
|
||||||
dataset->findAndGetUint16(DcmTagKey(0x0028, 0x0010), header.Rows);
|
dataset->findAndGetSint32(DCM_SeriesNumber, fileHeader.SeriesNumber);
|
||||||
dataset->findAndGetUint16(DcmTagKey(0x0028, 0x0011), header.Columns);
|
|
||||||
|
|
||||||
dataset->findAndGetFloat64(DcmTagKey(0x0028, 0x1050), header.WindowCenter);
|
dataset->findAndGetSint32(DCM_AcquisitionNumber, fileHeader.AcquisitionNumber);
|
||||||
dataset->findAndGetFloat64(DcmTagKey(0x0028, 0x1051), header.WindowWidth);
|
|
||||||
|
|
||||||
|
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 =
|
auto spacingResult =
|
||||||
dataset->findAndGetFloat64(DcmTagKey(0x0028, 0x0030), header.Spacing[0], 0);
|
dataset->findAndGetFloat64(DCM_PixelSpacing, fileHeader.Spacing[0], 0);
|
||||||
dataset->findAndGetFloat64(DcmTagKey(0x0028, 0x0030), header.Spacing[1], 1);
|
dataset->findAndGetFloat64(DCM_PixelSpacing, fileHeader.Spacing[1], 1);
|
||||||
//No Pixel spacing , try use Imager Pixel Spacing to instead
|
//No Pixel spacing , try use Imager Pixel Spacing to instead
|
||||||
if (!spacingResult.good()){
|
if (!spacingResult.good()){
|
||||||
spacingResult =
|
spacingResult =
|
||||||
dataset->findAndGetFloat64(DcmTagKey(0x0018, 0x1164), header.Spacing[0], 0);
|
dataset->findAndGetFloat64(DCM_ImagerPixelSpacing, fileHeader.Spacing[0], 0);
|
||||||
dataset->findAndGetFloat64(DcmTagKey(0x0018, 0x1164), header.Spacing[1], 1);
|
dataset->findAndGetFloat64(DCM_ImagerPixelSpacing, fileHeader.Spacing[1], 1);
|
||||||
}
|
}
|
||||||
//No Imager Pixel Spacing, use default 1.0
|
//No Imager Pixel Spacing, use default 1.0
|
||||||
if (!spacingResult.good()){
|
if (!spacingResult.good()){
|
||||||
header.Spacing[0] = 1.0;
|
fileHeader.Spacing[0] = 1.0;
|
||||||
header.Spacing[1] = 1.0;
|
fileHeader.Spacing[1] = 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
dataset->findAndGetFloat64(DcmTagKey(0x0020, 0x0037), header.Orientation[0], 0);
|
//thickness
|
||||||
dataset->findAndGetFloat64(DcmTagKey(0x0020, 0x0037), header.Orientation[1], 1);
|
if (dataset->findAndGetFloat64(DCM_SliceThickness,fileHeader.Spacing[2]).bad()){
|
||||||
dataset->findAndGetFloat64(DcmTagKey(0x0020, 0x0037), header.Orientation[2], 2);
|
if (dataset->findAndGetFloat64(DCM_SpacingBetweenSlices,fileHeader.Spacing[2]).bad()){
|
||||||
dataset->findAndGetFloat64(DcmTagKey(0x0020, 0x0037), header.Orientation[3], 3);
|
fileHeader.Spacing[2] = 1.0;
|
||||||
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);
|
|
||||||
std::string uniqueID;
|
std::string uniqueID;
|
||||||
uniqueID.append(PatientName);
|
uniqueID.append(PatientName);
|
||||||
uniqueID.append("_");
|
uniqueID.append("_");
|
||||||
uniqueID.append(StudyUID);
|
uniqueID.append(StudyUID);
|
||||||
uniqueID.append("_");
|
uniqueID.append("_");
|
||||||
uniqueID.append(SeriesUID);
|
uniqueID.append(fileHeader.SeriesUID);
|
||||||
header.SeriesUID = SeriesUID;
|
|
||||||
// series be firstly loaded
|
// series be firstly loaded
|
||||||
if (series.count(uniqueID)==0) {
|
if (series.count(uniqueID)==0) {
|
||||||
series.emplace(uniqueID,DICOMFileList());
|
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() {
|
void DICOMHeaderHelper::ArrangeSeriesProperty() {
|
||||||
for (auto item : series) {
|
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) {
|
std::sort(item.second.begin(), item.second.end(), [](auto v1, auto v2) {
|
||||||
return v1.SeriesNumber != v2.SeriesNumber ? (v1.SeriesNumber < v2.SeriesNumber) :
|
return v1.SeriesNumber != v2.SeriesNumber ? (v1.SeriesNumber < v2.SeriesNumber) :
|
||||||
(v1.AcquisitionNumber != v2.AcquisitionNumber ? (v1.AcquisitionNumber < v2.AcquisitionNumber) :
|
(v1.AcquisitionNumber != v2.AcquisitionNumber ? (v1.AcquisitionNumber < v2.AcquisitionNumber) :
|
||||||
(v1.InstanceNumber < v2.InstanceNumber));
|
(v1.InstanceNumber < v2.InstanceNumber));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// split series into image set
|
||||||
auto currentValue = &(item.second[0]);
|
auto currentValue = &(item.second[0]);
|
||||||
std::vector<int> splitIndex;
|
std::vector<int> splitIndex;
|
||||||
for (int i = 1; i < item.second.size(); ++i) {
|
for (int i = 1; i < item.second.size(); ++i) {
|
||||||
@@ -218,10 +291,12 @@ void DICOMHeaderHelper::ArrangeSeriesProperty() {
|
|||||||
splitIndex.push_back(i);
|
splitIndex.push_back(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//sort every image set
|
|
||||||
|
// use image set to create image property
|
||||||
int imageSetNumber = 0;
|
int imageSetNumber = 0;
|
||||||
int beginOffset = 0;
|
int beginOffset = 0;
|
||||||
for (int j = 0; j < splitIndex.size(); ++j) {
|
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],
|
std::sort(item.second.begin() + beginOffset, item.second.begin() + splitIndex[j],
|
||||||
[](auto v1, auto v2) {
|
[](auto v1, auto v2) {
|
||||||
return v1.image_position < v2.image_position;
|
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) {
|
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];
|
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->SetRows(header.Rows);
|
||||||
property->SetColumns(header.Columns);
|
property->SetColumns(header.Columns);
|
||||||
property->SetSamplePerPixel(header.SamplePerPixel);
|
property->SetSamplePerPixel(header.SamplePerPixel);
|
||||||
property->SetSeriesNumber(header.SeriesNumber);
|
|
||||||
// property->SetImageNumber(item.second[beginOffset].InstanceNumber);
|
// window
|
||||||
property->SetAcquisitionNumber(header.AcquisitionNumber);
|
|
||||||
property->AddWindowLevelPreset(header.WindowWidth,
|
property->AddWindowLevelPreset(header.WindowWidth,
|
||||||
header.WindowCenter);
|
header.WindowCenter);
|
||||||
property->SetPosition(header.Position[0],header.Position[1], header.Position[2]);
|
|
||||||
|
|
||||||
property->SetDirectionCosine(header.Orientation[0],header.Orientation[1],header.Orientation[2],
|
// coordinate
|
||||||
header.Orientation[3],header.Orientation[4],header.Orientation[5]);
|
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;
|
DcmFileFormat file;
|
||||||
// read success!
|
// read success!
|
||||||
if (file.loadFile(header.FilePath).good()) {
|
if (file.loadFile(header.FilePath).good()) {
|
||||||
DcmDataset *dataset = file.getDataset();
|
DcmDataset *dataset = file.getDataset();
|
||||||
#define ReadTAGToProperty(Name, group, element)\
|
|
||||||
|
// try to read patient and study information
|
||||||
|
#define ReadTAGToProperty(Name)\
|
||||||
std::string Name;\
|
std::string Name;\
|
||||||
dataset->findAndGetOFString(DcmTagKey(group, element), Name);\
|
dataset->findAndGetOFString(DCM_##Name, Name);\
|
||||||
property->Set##Name(Name.c_str());
|
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)
|
std::string StudyUID;
|
||||||
ReadTAGToProperty(PatientSex, 0x0010, 0x0040)
|
dataset->findAndGetOFString(DCM_StudyInstanceUID, StudyUID);
|
||||||
ReadTAGToProperty(PatientAge, 0x0010, 0x1010)
|
property->SetStudyUID(StudyUID.c_str());
|
||||||
ReadTAGToProperty(StudyDate, 0x0008, 0x0020)
|
|
||||||
ReadTAGToProperty(AcquisitionDate, 0x0008, 0x0022)
|
|
||||||
ReadTAGToProperty(StudyTime, 0x0008, 0x0030)
|
|
||||||
ReadTAGToProperty(AcquisitionTime, 0x0008, 0x0032)
|
|
||||||
ReadTAGToProperty(Modality, 0x0008, 0x0060)
|
|
||||||
|
|
||||||
ReadTAGToProperty(StudyUID, 0x0020, 0x000d)
|
// check has overlay
|
||||||
ReadTAGToProperty(StudyID, 0x0020, 0x0010)
|
unsigned short overlayRows = 0;
|
||||||
|
dataset->findAndGetUint16(DCM_OverlayRows, overlayRows);
|
||||||
|
if (overlayRows > 0){
|
||||||
|
property->SetHasOverlay(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert text to utf-8 codec
|
||||||
std::string charSet = "";
|
std::string charSet = "";
|
||||||
auto needConvert = dataset->findAndGetOFString(DcmTagKey(0x0008, 0x0005), charSet);
|
auto needConvert = dataset->findAndGetOFString(DcmTagKey(0x0008, 0x0005), charSet);
|
||||||
if (needConvert.good()){
|
if (needConvert.good()){
|
||||||
DcmSpecificCharacterSet charSetConvert;
|
DcmSpecificCharacterSet charSetConvert;
|
||||||
charSetConvert.selectCharacterSet(charSet);
|
charSetConvert.selectCharacterSet(charSet);
|
||||||
#define ReadTAGToPropertyConvert(Name, group, element)\
|
|
||||||
|
#define ReadTAGToPropertyConvert(Name)\
|
||||||
std::string Name;\
|
std::string Name;\
|
||||||
std::string Name##Convert;\
|
std::string Name##Convert;\
|
||||||
dataset->findAndGetOFString(DcmTagKey(group, element), Name);\
|
dataset->findAndGetOFString(DCM_##Name, Name);\
|
||||||
charSetConvert.convertString(Name,Name##Convert);\
|
charSetConvert.convertString(Name,Name##Convert);\
|
||||||
property->Set##Name(Name##Convert.c_str());
|
property->Set##Name(Name##Convert.c_str());
|
||||||
|
|
||||||
ReadTAGToPropertyConvert(PatientID, 0x0010, 0x0020)
|
ReadTAGToPropertyConvert(PatientID)
|
||||||
ReadTAGToPropertyConvert(PatientName, 0x0010, 0x0010)
|
ReadTAGToPropertyConvert(PatientName)
|
||||||
ReadTAGToPropertyConvert(InstitutionName, 0x0008, 0x0080)
|
ReadTAGToPropertyConvert(InstitutionName)
|
||||||
ReadTAGToPropertyConvert(StudyDescription, 0x0008, 0x1030)
|
ReadTAGToPropertyConvert(StudyDescription)
|
||||||
ReadTAGToPropertyConvert(SeriesDescription, 0x0008, 0x103E)
|
ReadTAGToPropertyConvert(SeriesDescription)
|
||||||
} else{
|
} else{
|
||||||
ReadTAGToProperty(PatientID, 0x0010, 0x0020)
|
ReadTAGToProperty(PatientID)
|
||||||
ReadTAGToProperty(PatientName, 0x0010, 0x0010)
|
ReadTAGToProperty(PatientName)
|
||||||
ReadTAGToProperty(InstitutionName, 0x0008, 0x0080)
|
ReadTAGToProperty(InstitutionName)
|
||||||
ReadTAGToProperty(StudyDescription, 0x0008, 0x1030)
|
ReadTAGToProperty(StudyDescription)
|
||||||
ReadTAGToProperty(SeriesDescription, 0x0008, 0x103E)
|
ReadTAGToProperty(SeriesDescription)
|
||||||
}
|
}
|
||||||
double thickness= 1.0;
|
|
||||||
dataset->findAndGetFloat64(DcmTagKey(0x0008, 0x0050), thickness);
|
// read spacing for image data
|
||||||
double RescaleSlope= 1.0;
|
double thickness = header.Spacing[2];
|
||||||
dataset->findAndGetFloat64(DcmTagKey(0x0028, 0x1053), RescaleSlope);
|
|
||||||
double RescaleOffset= 0.0;
|
// no frame thickness set, read first file thickness
|
||||||
dataset->findAndGetFloat64(DcmTagKey(0x0028, 0x1052), RescaleOffset);
|
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;
|
unsigned short bitsAllocated= 1;
|
||||||
dataset->findAndGetUint16(DcmTagKey(0x0028, 0x0100), bitsAllocated);
|
dataset->findAndGetUint16(DcmTagKey(0x0028, 0x0100), bitsAllocated);
|
||||||
unsigned short PixelRepresentation= 0;
|
unsigned short PixelRepresentation= 0;
|
||||||
dataset->findAndGetUint16(DcmTagKey(0x0028, 0x0103), PixelRepresentation);
|
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->SetBitsAllocated(bitsAllocated);
|
||||||
property->SetPixelRepresentation(PixelRepresentation);
|
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{
|
else{
|
||||||
printf( " file:%s load error!\r\b", header.FilePath.c_str() );
|
printf( " file:%s load error!\r\b", header.FilePath.c_str() );
|
||||||
@@ -337,9 +470,12 @@ ExtendMedicalImageProperties* DICOMHeaderHelper::createProperty(const std::vecto
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
if (splitIndex-beginOffset>1){
|
if (splitIndex-beginOffset>1){
|
||||||
|
// try to use real world slice origin point distance to instead thickness
|
||||||
const DICOMFileHeader& header2 = headerList[beginOffset+1];
|
const DICOMFileHeader& header2 = headerList[beginOffset+1];
|
||||||
double spacing2 = sqrt(vtkMath::Distance2BetweenPoints(header.Position,header2.Position));
|
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;
|
return property;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,9 @@ struct DICOMFileHeader {
|
|||||||
long SeriesNumber = 0;
|
long SeriesNumber = 0;
|
||||||
long AcquisitionNumber = 0;
|
long AcquisitionNumber = 0;
|
||||||
long InstanceNumber = 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 Rows;
|
||||||
unsigned short Columns;
|
unsigned short Columns;
|
||||||
unsigned short SamplePerPixel;
|
unsigned short SamplePerPixel;
|
||||||
@@ -27,6 +29,25 @@ struct DICOMFileHeader {
|
|||||||
double Position[3] = {.0, .0, .0};
|
double Position[3] = {.0, .0, .0};
|
||||||
double Orientation[6] = {.0, .0, .0, .0, .0, .0};
|
double Orientation[6] = {.0, .0, .0, .0, .0, .0};
|
||||||
double image_position = 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) {
|
bool equalImageSet(const DICOMFileHeader &v) {
|
||||||
return Rows == v.Rows && Columns == v.Columns &&
|
return Rows == v.Rows && Columns == v.Columns &&
|
||||||
|
|||||||
@@ -14,6 +14,8 @@
|
|||||||
#include "openjpeg-2.3/openjpeg.h"
|
#include "openjpeg-2.3/openjpeg.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "DICOMFileObjectCache.h"
|
||||||
|
|
||||||
void DICOMPixelDataHelper::InitCodecs() {
|
void DICOMPixelDataHelper::InitCodecs() {
|
||||||
DcmRLEDecoderRegistration::registerCodecs(OFFalse, OFTrue);//注册解码器
|
DcmRLEDecoderRegistration::registerCodecs(OFFalse, OFTrue);//注册解码器
|
||||||
/// register JPEG decompression codecs
|
/// register JPEG decompression codecs
|
||||||
@@ -42,7 +44,7 @@ void DICOMPixelDataHelper::GetThumbnailData(ExtendMedicalImageProperties *proper
|
|||||||
DcmFileFormat *fileFormat = new DcmFileFormat();//读取文件获取传输语法
|
DcmFileFormat *fileFormat = new DcmFileFormat();//读取文件获取传输语法
|
||||||
if (fileFormat->loadFile(property->GetThumbnailFileName()).good()) {
|
if (fileFormat->loadFile(property->GetThumbnailFileName()).good()) {
|
||||||
DcmDataset *dset = fileFormat->getDataset();
|
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);
|
DicomImage *sdcmImage = dcmImage.createScaledImage(100.0, 0.0, 0, 1);
|
||||||
sdcmImage->setWindow(wl, ww);
|
sdcmImage->setWindow(wl, ww);
|
||||||
sdcmImage->showAllOverlays();
|
sdcmImage->showAllOverlays();
|
||||||
@@ -50,6 +52,7 @@ void DICOMPixelDataHelper::GetThumbnailData(ExtendMedicalImageProperties *proper
|
|||||||
unsigned char *outputData = (unsigned char *) sdcmImage->getOutputData(8);//按8位的位宽取数据
|
unsigned char *outputData = (unsigned char *) sdcmImage->getOutputData(8);//按8位的位宽取数据
|
||||||
data = new unsigned char[length];
|
data = new unsigned char[length];
|
||||||
memcpy(data, outputData, length);
|
memcpy(data, outputData, length);
|
||||||
|
sdcmImage->deleteOutputData();
|
||||||
delete sdcmImage;
|
delete sdcmImage;
|
||||||
}
|
}
|
||||||
delete fileFormat;
|
delete fileFormat;
|
||||||
@@ -155,50 +158,45 @@ void DICOMPixelDataHelper::GetRGBPixelData(const char *path, void *data, unsigne
|
|||||||
}
|
}
|
||||||
|
|
||||||
int DICOMPixelDataHelper::GetOverLayCount(const char *path) {
|
int DICOMPixelDataHelper::GetOverLayCount(const char *path) {
|
||||||
int ret = 0;
|
int ret =0;
|
||||||
DcmFileFormat fileFormat;
|
DcmFileFormat fileFormat;
|
||||||
if (fileFormat.loadFile(path).good()) {
|
if (fileFormat.loadFile(path).good()) {
|
||||||
DcmDataset * dset = fileFormat.getDataset();
|
DcmDataset * dset = fileFormat.getDataset();
|
||||||
DicomImage dcmImage(&fileFormat, dset->getOriginalXfer(), CIF_MayDetachPixelData);
|
DicomImage dcmImage(&fileFormat, dset->getOriginalXfer(), CIF_UsePartialAccessToPixelData);
|
||||||
ret = dcmImage.getOverlayCount();
|
ret = dcmImage.getOverlayCount();
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DICOMPixelDataHelper::GetOverlayData(const char *path, void *data,
|
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();//读取文件获取传输语法
|
DcmFileFormat *fileFormat = new DcmFileFormat();//读取文件获取传输语法
|
||||||
if (fileFormat->loadFile(path).good()) {
|
if (fileFormat->loadFile(path).good()) {
|
||||||
DcmDataset *dset = fileFormat->getDataset();
|
DcmDataset *dset = fileFormat->getDataset();
|
||||||
DicomImage dcmImage(fileFormat, dset->getOriginalXfer(), CIF_MayDetachPixelData);
|
DicomImage dcmImage(fileFormat, dset->getOriginalXfer(), CIF_MayDetachPixelData);
|
||||||
if(dcmImage.getOverlayCount()>plane){
|
for (int j = 0; j < dcmImage.getOverlayCount(); ++j) {
|
||||||
const void * overlay = dcmImage.getFullOverlayData(plane,width, height);
|
const void *overlay = dcmImage.getFullOverlayData(j, width, height);
|
||||||
int dataLength = width*height;
|
int dataLength = width * height;
|
||||||
if (plane == 0){
|
if (j == 0) {
|
||||||
memcpy(data, overlay, dataLength);
|
memcpy(data, overlay, dataLength);
|
||||||
}
|
} else {
|
||||||
else{
|
|
||||||
int count32 = dataLength / 4;
|
int count32 = dataLength / 4;
|
||||||
int tailCount8 = dataLength % 4;
|
int tailCount8 = dataLength % 4;
|
||||||
//OR operation header and content
|
//OR operation header and content
|
||||||
Uint32 * mergeDst = (Uint32*)data;
|
Uint32 *mergeDst = (Uint32 *) data;
|
||||||
Uint32 * mergeSrc = (Uint32*)overlay;
|
Uint32 *mergeSrc = (Uint32 *) overlay;
|
||||||
for (int i = 0; i < count32; ++i) {
|
for (int i = 0; i < count32; ++i) {
|
||||||
mergeDst[i] = mergeDst[i] | mergeSrc[i];
|
mergeDst[i] = mergeDst[i] | mergeSrc[i];
|
||||||
}
|
}
|
||||||
//OR operation tail
|
//OR operation tail
|
||||||
Uint8 * mergeDst8 = (Uint8*)data;
|
Uint8 *mergeDst8 = (Uint8 *) data;
|
||||||
Uint8 * mergeSrc8 = (Uint8*)overlay;
|
Uint8 *mergeSrc8 = (Uint8 *) overlay;
|
||||||
for (int i = 1; i <= tailCount8; ++i) {
|
for (int i = 1; i <= tailCount8; ++i) {
|
||||||
mergeDst8[dataLength - i] = mergeDst8[dataLength - i] | mergeSrc8[dataLength - i];
|
mergeDst8[dataLength - i] = mergeDst8[dataLength - i] | mergeSrc8[dataLength - i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dcmImage.deleteOverlayData();
|
dcmImage.deleteOverlayData();
|
||||||
}
|
}
|
||||||
else{
|
|
||||||
width=-1;
|
|
||||||
height=-1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
width=-1;
|
width=-1;
|
||||||
@@ -206,3 +204,53 @@ void DICOMPixelDataHelper::GetOverlayData(const char *path, void *data,
|
|||||||
}
|
}
|
||||||
delete fileFormat;
|
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);
|
||||||
|
}
|
||||||
|
|||||||
@@ -26,6 +26,14 @@ public:
|
|||||||
*/
|
*/
|
||||||
static void GetPixelData(const char * path, void* data, unsigned long& length);
|
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.
|
* Get Pixel Data with Rescaled, result is calculate as Float, and arrange as Float.
|
||||||
* @param path dcm file absolute path
|
* @param path dcm file absolute path
|
||||||
@@ -45,7 +53,7 @@ public:
|
|||||||
static int GetOverLayCount(const char *path);
|
static int GetOverLayCount(const char *path);
|
||||||
|
|
||||||
static void GetOverlayData(const char * path, void* data,
|
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.
|
* Init decompress Codecs, must be called before other method.
|
||||||
|
|||||||
@@ -65,11 +65,11 @@ public:
|
|||||||
|
|
||||||
vtkGetVector3Macro(ZVector,double);
|
vtkGetVector3Macro(ZVector,double);
|
||||||
|
|
||||||
const std::vector<std::string>& GetFileNames(){
|
const std::vector<std::pair<std::string,long>>& GetFileNames(){
|
||||||
return FileNames;
|
return FileNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetFileNames(std::vector<std::string>& files){
|
void SetFileNames(std::vector<std::pair<std::string,long>>& files){
|
||||||
FileNames = std::move(files);
|
FileNames = std::move(files);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,7 +88,7 @@ public:
|
|||||||
|
|
||||||
const char* GetThumbnailFileName(){
|
const char* GetThumbnailFileName(){
|
||||||
if (FileNames.empty()) return nullptr;
|
if (FileNames.empty()) return nullptr;
|
||||||
return FileNames[0].data();
|
return FileNames[0].first.data();
|
||||||
}
|
}
|
||||||
unsigned long long GetSliceCount(){
|
unsigned long long GetSliceCount(){
|
||||||
return FileNames.size();
|
return FileNames.size();
|
||||||
@@ -135,7 +135,7 @@ protected:
|
|||||||
unsigned short BitsAllocated;
|
unsigned short BitsAllocated;
|
||||||
unsigned short PixelRepresentation;
|
unsigned short PixelRepresentation;
|
||||||
bool HasOverlay = false;
|
bool HasOverlay = false;
|
||||||
std::vector<std::string> FileNames;
|
std::vector<std::pair<std::string,long>> FileNames;
|
||||||
std::string uniqueID;
|
std::string uniqueID;
|
||||||
vtkNew<vtkMatrix4x4> OrientationMatrix;
|
vtkNew<vtkMatrix4x4> OrientationMatrix;
|
||||||
vtkNew<vtkMatrix4x4> WorldToModelMatrix;
|
vtkNew<vtkMatrix4x4> WorldToModelMatrix;
|
||||||
@@ -143,8 +143,6 @@ protected:
|
|||||||
private:
|
private:
|
||||||
ExtendMedicalImageProperties(const ExtendMedicalImageProperties&) = delete;
|
ExtendMedicalImageProperties(const ExtendMedicalImageProperties&) = delete;
|
||||||
void operator=(const ExtendMedicalImageProperties&) = delete;
|
void operator=(const ExtendMedicalImageProperties&) = delete;
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -98,9 +98,9 @@ void vtkDICOMImageReader2::ExecuteDataWithInformation(vtkDataObject *output,
|
|||||||
DICOMPixelDataHelper::InitCodecs();
|
DICOMPixelDataHelper::InitCodecs();
|
||||||
|
|
||||||
for (int i = 0; i < properties->GetFileNames().size(); ++i) {
|
for (int i = 0; i < properties->GetFileNames().size(); ++i) {
|
||||||
const std::string &path = properties->GetFileNames()[i];
|
const std::string &path = properties->GetFileNames()[i].first;
|
||||||
int overlayCount = DICOMPixelDataHelper::GetOverLayCount(path.c_str());
|
const long &frameIndex = properties->GetFileNames()[i].second;
|
||||||
if (overlayCount > 0){
|
if (properties->GetHasOverlay()){
|
||||||
if (!OverLayData){
|
if (!OverLayData){
|
||||||
HasOverlay = true;
|
HasOverlay = true;
|
||||||
OverLayData = vtkImageData::New();
|
OverLayData = vtkImageData::New();
|
||||||
@@ -112,9 +112,8 @@ void vtkDICOMImageReader2::ExecuteDataWithInformation(vtkDataObject *output,
|
|||||||
unsigned int width = 0;
|
unsigned int width = 0;
|
||||||
unsigned int height = 0;
|
unsigned int height = 0;
|
||||||
void* overlayBuffer = OverLayData->GetScalarPointer(0, 0, i);
|
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);
|
DICOMPixelDataHelper::GetRGBPixelData(path.c_str(), buffer, imageDataLength);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
if (frameIndex>=0){
|
||||||
DICOMPixelDataHelper::GetPixelData(path.c_str(), buffer, imageDataLength);
|
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;
|
buffer = ((char *) buffer) + imageDataLength;
|
||||||
}
|
}
|
||||||
|
DICOMPixelDataHelper::ClearFileObject();
|
||||||
// finalize codecs
|
// finalize codecs
|
||||||
DICOMPixelDataHelper::FinalizeCodecs();
|
DICOMPixelDataHelper::FinalizeCodecs();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user